New main site, maybe?

This commit is contained in:
MeowcaTheoRange 2023-11-23 02:53:28 -06:00
commit a76f50d3ec
31 changed files with 1418 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules/
package-lock.json
output/

12
debug.js Normal file
View file

@ -0,0 +1,12 @@
const express = require("express");
const app = express();
app.use(
express.static("output", {
extensions: ["html"],
index: "index.html",
redirect: true,
})
);
app.listen(3000);

30
embeds/loader.html Normal file
View file

@ -0,0 +1,30 @@
<div
style="
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--background-color);
color: var(--color);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
z-index: 1000;
font-family: sans-serif;
"
id="loading"
>
<div>
<h1>Loading...</h1>
<h2>Please wait.</h2>
<p>If this page does not load, try enabling JavaScript.</p>
<small>(This site won't work well without it.)</small>
<script>
window.addEventListener("load", function () {
document.querySelector("#loading").style.display = "none";
});
</script>
</div>
</div>

7
embeds/nav.html Normal file
View file

@ -0,0 +1,7 @@
<section>
<p>
<a href="/">Home</a> - <a href="/projects/">Projects</a> -
<a href="/about/">About</a> -
<a href="/site/">Site Information</a>
</p>
</section>

25
gulpfile.js Normal file
View file

@ -0,0 +1,25 @@
const { src, dest } = require("gulp");
const through2 = require("through2");
const fs = require("fs");
const path = require("path");
exports.default = () => {
return src("views/**/*")
.pipe(
through2.obj(function (file, _, cb) {
if (file.isBuffer()) {
file.contents = Buffer.from(
file.contents
.toString()
.replace(/<\$ (.*?) \$>/gm, function (m, g1) {
return fs.readFileSync(path.join(__dirname, "embeds", g1), {
encoding: "utf-8",
});
})
);
}
cb(null, file);
})
)
.pipe(dest("output/"));
};

14
package.json Normal file
View file

@ -0,0 +1,14 @@
{
"name": "projects-windowed",
"version": "1.0.0",
"scripts": {
"dev": "nodemon --exec 'gulp;node debug.js' --ext '*' --ignore 'output/*'"
},
"devDependencies": {
"gulp": "^4.0.2"
},
"dependencies": {
"express": "^4.18.2",
"through2": "^4.0.2"
}
}

41
views/about/index.html Normal file
View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MeowcaTheoRange</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100;200;300;400;500;600;700;800;900&display=swap");
:root {
--base-scale: 16px;
--background-color: hsl(195, 100%, 15%);
--color: hsl(195, 100%, 85%);
--accent-color: hsl(195, 100%, 50%);
--accent-color-fg: hsl(195, 100%, 95%);
--font-family: "Lexend Deca";
--document-width: 40em;
}
</style>
</head>
<body>
<header>
<section>
<h1>About</h1>
<p>More about me.</p>
</section>
<$ nav.html $>
</header>
<main>
<section>
<h2>WIP</h2>
</section>
</main>
<section id="accessibility"></section>
<script src="/scripts/windows.js"></script>
<script src="/scripts/accessibility.js"></script>
</body>
</html>

80
views/index.html Executable file
View file

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MeowcaTheoRange</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100;200;300;400;500;600;700;800;900&display=swap");
:root {
--base-scale: 16px;
--background-color: hsl(195, 100%, 15%);
--color: hsl(195, 100%, 85%);
--accent-color: hsl(195, 100%, 50%);
--accent-color-fg: hsl(195, 100%, 95%);
--font-family: "Lexend Deca";
--document-width: 40em;
}
</style>
</head>
<body>
<header>
<section>
<h1>ABTMTR.LINK</h1>
</section>
<$ nav.html $>
</header>
<main>
<section>
<h2>Welcome! 👋</h2>
<p>I'm MeowcaTheoRange.</p>
<p>
I'm a web developer, Fediverse enthusiast, and compulsory Minnesotan.
</p>
<p>I run this domain and all of the services on it!</p>
</section>
<section>
<h2>What I like doing</h2>
<p>
My favourite hobbies are <b>programming</b>, <b>drawing</b>,
<b>occasionally making small bits of music</b>,
<b>obsessing over fonts</b>, and <b>being pedantic</b>.
</p>
</section>
<section>
<h2>What I'd like you to know about me</h2>
<ul>
<li>Please be patient with me.</li>
<li>
Please be understanding! Ask me for clarification if required.
</li>
<li>
I don't really like small talk - keep if brief if you want to check
up on me, please.
</li>
<li>
I'm not one to pick sides at first, usually. Being an "all or
nothing" kind of person isn't my thing, and if you don't like that,
feel free to tell me why your side is good.
</li>
<li>
I like getting tangled up in drama, but I'm not a spiteful person -
I'm usually only in it for the correlations.
</li>
<li>
You may see me hyperfixate on random stuff, like
<a href="#">certain fonts</a>
or public transit.
</li>
</ul>
</section>
</main>
<section id="accessibility"></section>
<script src="/scripts/windows.js"></script>
<script src="/scripts/accessibility.js"></script>
</body>
</html>

80
views/projects/hex/index.html Executable file
View file

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HexFlagGen</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100;200;300;400;500;600;700;800;900&display=swap");
:root {
--base-scale: 16px;
--background-color: hsl(0, 0%, 15%);
--color: hsl(0, 0%, 85%);
--accent-color: #808080;
--accent-color-fg: hsl(0, 0%, 95%);
--font-family: "Lexend Deca";
--document-width: 40em;
--border-style: 0.0625em solid var(--color);
}
</style>
</head>
<body>
<$ loader.html $>
<section>
<h1>HexFlagGen</h1>
<p>Make a flag out of the hexadecimal bytes of your choice.</p>
</section>
<hr />
<section>
<label for="data">Generator string</label><br />
<input
type="text"
id="data"
value="5BCEFAF5A9B8FFFFFFF5A9B85BCEFABA53"
style="width: 100%"
/><br />
<button id="generateButton">Generate!</button>
</section>
<section>
<input
type="number"
id="size_width"
style="width: 8ch"
min="0"
max="3000"
value="300"
/>
<label for="size_width">Width (px)</label><br />
<input
type="number"
id="size_height"
style="width: 8ch"
min="0"
max="3000"
value="200"
/>
<label for="size_height">Height (px)</label><br />
<select id="flag_type">
<option value="horiz">Horizontal</option>
<option value="vert" selected>Vertical</option>
</select>
<label for="flag_type">Flag type</label>
</section>
<hr />
<section>
<p>
Hex: <code><span id="hexdisplay">...</span></code>
</p>
<canvas id="canvas" width="300" height="200"></canvas>
</section>
<section id="accessibility"></section>
<script src="scripts/index.js"></script>
<script src="/scripts/accessibility.js"></script>
<script src="/scripts/interface.js"></script>
</body>
</html>

View file

@ -0,0 +1,117 @@
const canvas = document.getElementById("canvas");
const data = document.getElementById("data");
const generateButton = document.getElementById("generateButton");
const hexdisplay = document.getElementById("hexdisplay");
const size_width = document.getElementById("size_width");
const size_height = document.getElementById("size_height");
const flag_type = document.getElementById("flag_type");
const root = document.querySelector(":root");
const ctx = canvas.getContext("2d");
function getData() {
data.value = data.value.toUpperCase().replace(/[^0-9A-F]*/g, "");
var hexValue = data.value.match(/.{1,2}/g).map((x) => x.padStart(2, "0"));
hexdisplay.innerHTML = hexValue.join(" ");
createFlag(
hexValue.join("").match(/.{1,6}/g),
size_width.value,
size_height.value,
flag_type.value
);
var ac = averageColors(hexValue);
console.log(ac);
root.style.setProperty(
"--background-color",
arrayToColor(darkenColor(ac, 70))
);
root.style.setProperty("--color", arrayToColor(lightenColor(ac, 70)));
root.style.setProperty("--accent-color", arrayToColor(ac));
root.style.setProperty(
"--accent-color-fg",
arrayToColor(lightenColor(ac, 90))
);
propagateStyles(getComputedStyle(root));
}
generateButton.addEventListener("click", getData);
function averageColors(hex) {
var averageSet = [[], [], []];
hex.forEach((number, index) => {
averageSet[index % 3].push(parseInt(number, 16));
});
return averageSet.map((array) =>
Math.floor(array.reduce((a, b) => a + b) / array.length)
);
}
function createFlag(hex, w, h, type) {
canvas.width = w;
canvas.height = h;
// Initialize stuff
var width = ctx.canvas.width;
var height = ctx.canvas.height;
var textHeight = 24;
ctx.font = `${textHeight}px "Lexend Deca"`;
ctx.clearRect(0, 0, width, height);
console.log(hex);
var hexColors = [];
var excessHex = "";
hex.forEach((compound) => {
if (compound.length < 6) excessHex = compound;
else hexColors.push(compound);
});
var rectWidth = width / hexColors.length;
var rectHeight = height / hexColors.length;
for (color in hexColors) {
ctx.fillStyle = "#" + hexColors[color];
if (type === "horiz") ctx.fillRect(rectWidth * color, 0, rectWidth, height);
else if (type === "vert")
ctx.fillRect(0, rectHeight * color, width, rectHeight);
}
if (excessHex.length > 0) {
var text = "+ " + excessHex.match(/.{1,2}/g).join(" ");
var textWidth = ctx.measureText(text).width;
ctx.strokeStyle = "black";
ctx.lineWidth = 4;
ctx.fillStyle = "white";
ctx.strokeText(text, width - (textWidth + 8), height - textHeight / 2);
ctx.fillText(text, width - (textWidth + 8), height - textHeight / 2);
}
}
const clamp = (n, mi, ma) => Math.max(mi, Math.min(n, ma));
function lightenColor(color, mult) {
return [
clamp(color[0] + (mult / 100) * (255 - color[0]), 0, 255),
clamp(color[1] + (mult / 100) * (255 - color[1]), 0, 255),
clamp(color[2] + (mult / 100) * (255 - color[2]), 0, 255),
];
}
function darkenColor(color, mult) {
return [
clamp(color[0] - (mult / 100) * color[0], 0, 255),
clamp(color[1] - (mult / 100) * color[1], 0, 255),
clamp(color[2] - (mult / 100) * color[2], 0, 255),
];
}
function arrayToColor(color) {
return (
"#" + color.map((x) => Math.floor(x).toString(16).padStart(2, "0")).join("")
);
}

51
views/projects/index.html Executable file
View file

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Project Windows</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<link rel="stylesheet" href="/styles/windows.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100;200;300;400;500;600;700;800;900&display=swap");
:root {
--base-scale: 16px;
--background-color: hsl(15, 100%, 15%);
--color: hsl(15, 100%, 85%);
--accent-color: hsl(15, 100%, 50%);
--accent-color-fg: hsl(15, 100%, 95%);
--font-family: "Lexend Deca";
--document-width: 40em;
}
</style>
</head>
<body>
<$ loader.html $>
<section>
<h1>Projects</h1>
<p>Various projects I've made.</p>
</section>
<$ nav.html $>
<section>
<button
onclick="
new WindowObject(document.getElementById('WindowHolder'), '/projects/hex')"
>
Open Hex
</button>
<button
onclick="
new WindowObject(document.getElementById('WindowHolder'), '/projects/woz')"
>
Open Woz
</button>
</section>
<section id="accessibility"></section>
<div id="WindowHolder"></div>
<script src="/scripts/windows.js"></script>
<script src="/scripts/accessibility.js"></script>
</body>
</html>

82
views/projects/woz/index.html Executable file
View file

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WozSteamGem</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100;200;300;400;500;600;700;800;900&display=swap");
:root {
--base-scale: 16px;
--background-color: hsl(232, 95%, 15%);
--color: hsl(232, 95%, 85%);
--accent-color: #3a55fa;
--accent-color-fg: hsl(232, 95%, 95%);
--font-family: "Lexend Deca";
--document-width: 40em;
--border-style: 0.0625em solid var(--color);
}
</style>
</head>
<body>
<$ loader.html $>
<section>
<h1>WozSteamGem</h1>
<p>Make a Scott The Woz thumbnail out of any Steam game.</p>
</section>
<hr />
<section>
<input
type="number"
id="steam_game"
style="width: 8ch"
min="10"
value="620"
/>
<label for="steam_game">Steam Game ID</label><br />
<button id="generateButton">Generate!</button>
</section>
<section>
<label for="scott_index">Extras</label><br />
<input
type="number"
id="scott_index"
style="width: 8ch"
min="0"
max="9"
value="0"
/>
<label for="scott_index">Scott Index</label>
<small>
<label for="stash" class="checkbox">
<input
type="checkbox"
id="stash"
onchange="scott_index.disabled = event.target.checked"
/>
<span class="checkbox">O</span>
Scott's Stash
</label></small
><br />
<select id="align">
<option value="left">Left</option>
<option value="center" selected>Center</option>
<option value="right">Right</option>
</select>
<label for="align">Background Alignment</label>
</section>
<hr />
<section>
<canvas id="canvas" width="1280" height="720"></canvas>
</section>
<section id="accessibility"></section>
<script src="scripts/index.js"></script>
<script src="/scripts/accessibility.js"></script>
<script src="/scripts/interface.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View file

@ -0,0 +1,81 @@
const canvas = document.getElementById("canvas");
const data = document.getElementById("data");
const generateButton = document.getElementById("generateButton");
const hexdisplay = document.getElementById("hexdisplay");
const steam_game = document.getElementById("steam_game");
const scott_index = document.getElementById("scott_index");
const stash = document.getElementById("stash");
const align = document.getElementById("align");
const root = document.querySelector(":root");
const ctx = canvas.getContext("2d");
async function loadImage(url) {
return new Promise((r) => {
let img = new Image();
img.onload = () => r(img);
img.src = url;
});
}
async function getScott() {
var gameID = steam_game.value;
var scottID = scott_index.value;
var alignment = align.value;
const ctx = canvas.getContext("2d");
var hero = await loadImage(
`https://cdn.cloudflare.steamstatic.com/steam/apps/${gameID}/library_hero.jpg`
).catch(() => "404");
var scott = await loadImage(
stash.checked ? `public/wozstash.png` : `public/woz${scottID}.png`
).catch(() => "404");
var logo = await loadImage(
`https://cdn.cloudflare.steamstatic.com/steam/apps/${gameID}/logo.png`
).catch(() => "404");
if (scott === "404") {
console.log("Scott error: " + scottID);
return `Scott ID ${scottID} does not exist.`;
}
if (hero === "404" || logo === "404") {
console.log("404 error: " + gameID);
return `Steam ID ${gameID} does not have logo, hero image, or does not exist.`;
}
var xalign;
switch (alignment) {
case "left":
xalign = 0;
break;
case "right":
xalign = -950;
break;
default:
xalign = -505;
break;
}
console.log("Generating: " + gameID);
ctx.drawImage(hero, xalign, 0, 2229, 720);
ctx.drawImage(scott, 0, 0, 1280, 720);
const newHeight = 800 / (logo.width / logo.height);
ctx.fillStyle = "#000";
ctx.shadowColor = "#000";
ctx.shadowBlur = 16;
if (stash.checked)
ctx.drawImage(
logo,
400,
Math.min(560 - newHeight / 2, 669 - newHeight),
800,
newHeight
);
else
ctx.drawImage(logo, 64, Math.max(240 - newHeight / 2, 32), 800, newHeight);
}
generateButton.addEventListener("click", getScott);

68
views/scripts/accessibility.js Executable file
View file

@ -0,0 +1,68 @@
var accScale;
var accBase;
function createAccessibilityNodes() {
document.querySelector("#accessibility").innerHTML = `<fieldset>
<legend>Accessibility controls</legend>
<label for="size_width">
<input
id="acc-scale"
type="number"
style="width: 8ch"
min="8"
max="48"
value="16"
/>
UI scale</label><br />
<label for="acc-base" class="checkbox">
<input id="acc-base" type="checkbox" id="acc-base" />
<span class="checkbox"></span>
Base style (requires reload)
</label>
</fieldset>`;
accScale = document.getElementById("acc-scale");
accBase = document.getElementById("acc-base");
accScale.addEventListener("change", function (e) {
window.localStorage.setItem("acc-scale", e.target.value);
changeScale(e.target.value);
});
accBase.addEventListener("change", function (e) {
window.localStorage.setItem("acc-base", e.target.checked);
window.location.reload();
});
}
window.addEventListener("load", (e) => initializeChanges(e, true));
// on localStorage change
window.addEventListener("storage", initializeChanges);
let prevBase; // previous base, for reload check
function initializeChanges(_, loading) {
if (window.frameElement == null) createAccessibilityNodes();
const scale = window.localStorage.getItem("acc-scale");
const base = window.localStorage.getItem("acc-base");
changeScale(scale);
if (loading) {
prevBase = base;
changeBase(base === "true");
} else if (base != prevBase) window.location.reload();
if (scale != null && accScale != null) accScale.value = scale;
if (base != null && accBase != null) accBase.checked = base === "true";
}
function changeScale(scale) {
document.documentElement.style.setProperty("--base-scale", scale + "px");
}
function changeBase(base) {
if (base) document.documentElement.classList.add("base");
else document.documentElement.classList.remove("base");
}

22
views/scripts/interface.js Executable file
View file

@ -0,0 +1,22 @@
function propagateStyles(rootStyles) {
if (window.frameElement == null) return;
const wfParent = window.frameElement.parentElement;
wfParent.style.setProperty(
"--background-color",
rootStyles.getPropertyValue("--background-color")
);
wfParent.style.setProperty("--color", rootStyles.getPropertyValue("--color"));
wfParent.style.setProperty(
"--accent-color",
rootStyles.getPropertyValue("--accent-color")
);
wfParent.style.setProperty(
"--accent-color-fg",
rootStyles.getPropertyValue("--accent-color-fg")
);
wfParent.style.setProperty("--border-style", "0.0625em solid var(--color)");
}
window.addEventListener("load", () => {
propagateStyles(getComputedStyle(document.documentElement));
});

223
views/scripts/windows.js Executable file
View file

@ -0,0 +1,223 @@
class WindowObject {
windowObject;
windowManager;
windowManagerLabel;
windowContent;
#orig_mousePosX;
#orig_mousePosY;
#orig_selfPosX;
#orig_selfPosY;
#isDragging;
constructor(parent, content) {
this.parentElement = parent;
this.windowObject = WindowObject.createWindow(this, content);
this.parentElement.appendChild(this.windowObject);
WindowObject.raiseWindow(this.windowObject);
this.windowManager = this.windowObject.querySelector(".window-manager");
this.windowManagerLabel = this.windowObject.querySelector(
".window-manager-label"
);
this.windowContent = this.windowObject.querySelector(".window-content");
this.windowManager.addEventListener("mousedown", (e) =>
this.event__dragMouseDown(e)
);
this.windowManager.addEventListener("mousemove", (e) =>
this.event__dragMouseMove(e)
);
this.windowManager.addEventListener("mouseup", (e) =>
this.event__dragMouseUp(e)
);
this.windowManager.addEventListener("mouseout", (e) =>
this.event__dragMouseMove(e)
);
this.windowObject.addEventListener("mousedown", (e) =>
WindowObject.raiseWindow(this.windowObject)
);
this.windowContent.addEventListener("load", () => {
this.title = this.windowContent.contentWindow.document.title;
this.windowManager
.querySelector(".window-new-button")
.addEventListener("click", () => {
window.open(this.content);
});
});
}
get title() {
return this.windowManagerLabel.textContent;
}
get content() {
return this.windowContent.src;
}
set title(content) {
this.windowManagerLabel.textContent = content;
}
set content(src) {
this.windowContent.src = src;
}
minimizeWindow() {
if (this.windowObject.classList.contains("minimized")) {
this.windowObject.classList.remove("minimized");
WindowObject.raiseWindow(this.windowObject);
} else {
this.windowObject.classList.add("minimized");
this.windowObject.style.zIndex = 50;
}
}
maximizeWindow() {
this.windowObject.classList.toggle("maximized");
WindowObject.raiseWindow(this.windowObject);
}
destroy() {
this.windowObject.classList.add("closed");
setTimeout(() => {
this.windowObject.remove();
var someWindow = this.parentElement.querySelector(
`.window-object:last-child`
);
if (someWindow != null) WindowObject.raiseWindow(someWindow);
}, 250);
}
event__dragMouseDown(e) {
e.preventDefault();
this.#orig_mousePosX = e.clientX;
this.#orig_mousePosY = e.clientY;
this.#orig_selfPosX = this.windowObject.offsetLeft;
this.#orig_selfPosY = this.windowObject.offsetTop;
this.windowContent.style.userSelect = "none";
this.windowContent.style.pointerEvents = "none";
this.windowManager.style.cursor = "move";
this.#isDragging = true;
if (this.windowObject.style.zIndex != 100)
WindowObject.raiseWindow(this.windowObject);
}
event__dragMouseUp(e) {
e.preventDefault();
this.#orig_mousePosX = 0;
this.#orig_mousePosY = 0;
this.windowContent.style.userSelect = "auto";
this.windowContent.style.pointerEvents = "auto";
this.windowManager.style.cursor = "default";
this.#isDragging = false;
}
event__dragMouseMove(e) {
e.preventDefault();
if (this.#isDragging) {
this.windowObject.style.left =
this.#orig_selfPosX - (this.#orig_mousePosX - e.clientX) + "px";
this.windowObject.style.top =
this.#orig_selfPosY - (this.#orig_mousePosY - e.clientY) + "px";
}
}
static raiseWindow(windowObj) {
windowObj.parentElement.querySelectorAll(".window-object").forEach((x) => {
if (x.style.zIndex > 50) x.style.zIndex -= 1;
x.classList.add("unfocused");
});
windowObj.style.zIndex = 100;
windowObj.classList.remove("unfocused");
windowObj.focus();
}
static createWindow(windowRef, content) {
const windowObject = document.createElement("div");
windowObject.classList.add("window-object");
{
const windowManager = document.createElement("div");
windowManager.classList.add("window-manager");
{
const windowManagerStart = document.createElement("div");
windowManagerStart.classList.add("window-manager-start");
{
const windowManagerLabel = document.createElement("span");
windowManagerLabel.textContent = "";
windowManagerLabel.classList.add("window-manager-label");
windowManagerStart.appendChild(windowManagerLabel);
}
windowManager.appendChild(windowManagerStart);
}
{
const windowManagerEnd = document.createElement("div");
windowManagerEnd.classList.add("window-manager-end");
{
const windowNewButton = document.createElement("button");
windowNewButton.innerHTML = "open_in_new";
windowNewButton.classList.add("window-new-button");
windowManagerEnd.appendChild(windowNewButton);
}
{
const windowMaximizeButton = document.createElement("button");
windowMaximizeButton.innerHTML = "maximize";
windowMaximizeButton.classList.add("window-maximize-button");
windowMaximizeButton.ariaHidden = true; // esoteric operation to screen-reader users
windowMaximizeButton.addEventListener("click", () =>
windowRef.maximizeWindow()
);
windowManagerEnd.appendChild(windowMaximizeButton);
}
{
const windowDestroyButton = document.createElement("button");
windowDestroyButton.innerHTML = "close";
windowDestroyButton.classList.add("window-destroy-button");
windowDestroyButton.ariaHidden = true;
windowDestroyButton.addEventListener("click", () =>
windowRef.destroy()
);
windowManagerEnd.appendChild(windowDestroyButton);
}
windowManager.appendChild(windowManagerEnd);
}
windowObject.appendChild(windowManager);
}
{
const windowContent = document.createElement("iframe");
windowContent.classList.add("window-content");
{
windowContent.src = content;
}
windowObject.appendChild(windowContent);
}
return windowObject;
}
}

64
views/site/index.html Normal file
View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MeowcaTheoRange</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100;200;300;400;500;600;700;800;900&display=swap");
:root {
--base-scale: 16px;
--background-color: hsl(195, 100%, 15%);
--color: hsl(195, 100%, 85%);
--accent-color: hsl(195, 100%, 50%);
--accent-color-fg: hsl(195, 100%, 95%);
--font-family: "Lexend Deca";
--document-width: 40em;
}
</style>
</head>
<body>
<header>
<section>
<h1>Site Information</h1>
</section>
<$ nav.html $>
</header>
<main>
<section>
<h2>What happened to the previous site?</h2>
<p>
The previous site kinda sucked. It depended on client-side React for
the most basic of things, and was just overall kind of a mess of TSX
everywhere.
</p>
<p>
This site is represented semi-statically, which means there's nothing
new being generated on the backend.<br />
This site does not use React either, so everything the frontend runs
is vanilla JavaScript.
</p>
<h2>
What's with the <b>accessibility controls</b> on every page of the
site?
</h2>
<p>
Just because. If you want to make the text bigger or get rid of the
colors, you have that panel.
</p>
<h2>What font are you using for this site?</h2>
<p>
<b>Lexend Deca</b>, or your <b>system's default sans-serif font</b>,
depending on if you're using Base style.
</p>
</section>
</main>
<section id="accessibility"></section>
<script src="/scripts/windows.js"></script>
<script src="/scripts/accessibility.js"></script>
</body>
</html>

260
views/styles/normal.css Executable file
View file

@ -0,0 +1,260 @@
@import url("https://necolas.github.io/normalize.css/8.0.1/normalize.css");
/* Document form */
:root {
--base-scale: 16px;
--background-color: hsl(0, 0%, 15%);
--color: hsl(0, 0%, 85%);
--accent-color: #808080;
--accent-color-fg: hsl(0, 0%, 95%);
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
--document-width: 40em;
--border-style: 0.0625em solid var(--color);
}
body {
width: 100vw;
max-width: var(--document-width);
margin: auto;
padding-inline: 0.5em;
padding-block: 0.5em;
}
html.base {
--background-color: #fff !important;
--color: #000 !important;
--accent-color: #000 !important;
--accent-color-fg: #fff !important;
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol" !important;
--document-width: 40em;
--border-style: 0.0625em solid var(--color);
}
html.base body {
margin: 0;
}
/* Size and type normalize */
html {
font-size: var(--base-scale);
background-color: var(--background-color);
color: var(--color);
}
main {
margin-block: 1em;
}
h1 {
font-size: 2em;
line-height: 1.5em;
margin: 0;
margin-block: 0.125em;
}
h2 {
font-size: 1.5em;
line-height: 1.25em;
margin: 0;
margin-block: 0.25em;
}
h3 {
font-size: 1.1em;
line-height: 1.25em;
margin: 0;
margin-block: 0.25em;
}
p {
font-size: 1em;
line-height: 1.25em;
margin-inline: 0;
margin-block: 0.5em;
}
ul,
ol {
padding: 0;
padding-inline-start: 1em;
margin: 0;
margin-block: 0.5em;
}
li {
margin-inline: 0;
margin-block: 0.5em;
}
small {
font-size: 0.75em;
}
big {
font-size: 1.5em;
}
code {
font-size: 1em;
line-height: 1.25em;
font-family: monospace;
}
hr {
border: none;
border-top: var(--border-style);
background-color: transparent;
margin-inline: 0;
margin-block: 0.25em;
}
/* Form normalize */
html {
font-family: var(--font-family);
}
* {
box-sizing: border-box;
}
*:focus {
outline: 2px solid var(--accent-color-fg);
}
fieldset {
border: var(--border-style);
padding-inline: 0.5em;
padding-block: 0.25em;
margin-inline: 0;
margin-block: 0.5em;
font-size: 1em;
background-color: var(--background-color);
color: var(--color);
}
/* Interactive normalize */
a {
color: var(--accent-color);
}
a:hover,
a:focus {
opacity: 0.8;
}
a:active {
opacity: 0.5;
}
input,
select,
textarea {
font-family: inherit;
border: var(--border-style);
padding-inline: 0.5em;
padding-block: 0.25em;
margin-inline: 0;
margin-block: 0.25em;
font-size: 1em;
background-color: var(--background-color);
color: var(--color);
}
input:disabled {
opacity: 0.5;
}
button,
input[type="button"],
input[type="submit"],
input[type="reset"] {
border: var(--border-style);
padding-inline: 0.5em;
padding-block: 0.25em;
margin-inline: 0;
margin-block: 0.25em;
font-size: 1em;
font-family: inherit;
background-color: var(--accent-color);
color: var(--accent-color-fg);
}
button[data-outlined],
input[type="button"][data-outlined],
input[type="submit"][data-outlined],
input[type="reset"][data-outlined] {
border: var(--border-style);
background-color: var(--background-color);
color: var(--accent-color);
}
button:hover,
input[type="button"]:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
button:focus,
input[type="button"]:focus,
input[type="submit"]:focus,
input[type="reset"]:focus {
opacity: 0.8;
}
button:active,
input[type="button"]:active,
input[type="submit"]:active,
input[type="reset"]:active {
opacity: 0.5;
}
/* -- Checkbox styling */
label.checkbox {
display: inline-block;
position: relative;
margin-inline: 0;
margin-block: 0.25em;
}
label.checkbox input[type="checkbox"] {
/* visibility: hidden; */
opacity: 0;
width: 0;
height: 0;
position: absolute;
top: 0;
left: 0;
}
label.checkbox input[type="checkbox"] + span.checkbox {
display: inline-block;
width: 1em;
height: 1em;
line-height: 1em;
padding-inline: 0.25em;
padding-block: 0.25em;
box-sizing: content-box;
border: var(--border-style);
color: transparent;
text-align: center;
user-select: none;
}
label.checkbox input[type="checkbox"]:focus + span.checkbox {
outline: 2px solid var(--accent-color-fg);
}
label.checkbox input[type="checkbox"]:checked + span.checkbox {
background-color: var(--accent-color);
border: var(--border-style);
color: var(--accent-color-fg);
}

11
views/styles/style.css Normal file
View file

@ -0,0 +1,11 @@
#accessibility {
position: sticky;
bottom: 1em;
left: 1em;
background-color: var(--background-color);
padding: 0.5em;
}
#accessibility fieldset {
margin-block: 0;
}

147
views/styles/windows.css Executable file
View file

@ -0,0 +1,147 @@
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0");
@keyframes appear {
from {
scale: 0;
opacity: 0;
}
to {
scale: 1;
opacity: 1;
}
}
div.window-object {
display: grid;
grid-template-rows: max-content auto;
grid-gap: 0.25em;
position: fixed;
top: calc(50vh - 150px);
left: calc(50vw - 200px);
width: 400px;
height: 300px;
min-width: 10em;
min-height: 2em;
background-color: var(--accent-color);
color: var(--accent-color-fg);
border: var(--border-style);
resize: both;
overflow: hidden;
padding: 0.25em;
/* padding: 0.5em; */
transition: opacity 0.25s, scale 0.25s;
scale: 1;
opacity: 1;
animation-name: appear;
animation-duration: 0.25s;
}
div.window-object.unfocused {
opacity: 0.5;
}
div.window-object.closed {
scale: 0;
opacity: 0;
}
div.window-object.minimized {
top: auto !important;
bottom: 0;
left: 0 !important;
max-width: 20em !important;
width: 20em !important;
min-width: 20em !important;
max-height: 2.5em !important;
height: 2.5em !important;
min-height: 2.5em !important;
resize: none;
}
div.window-object.minimized > div.window-manager {
cursor: default !important;
}
div.window-object.minimized > iframe.window-content {
display: none;
}
div.window-object.maximized {
top: 0 !important;
left: 0 !important;
max-width: 100vw !important;
width: 100vw !important;
min-width: 100vw !important;
max-height: 100vh !important;
height: 100vh !important;
min-height: 100vh !important;
resize: none;
}
div.window-object.maximized > div.window-manager {
cursor: default !important;
}
div.window-object > div.window-manager {
display: grid;
width: 100%;
grid-template-columns: auto max-content;
vertical-align: middle;
height: 1.5em;
overflow: hidden;
user-select: none;
}
div.window-object > div.window-manager > div {
overflow: hidden;
width: 100%;
vertical-align: middle;
height: 100%;
box-sizing: content-box;
}
div.window-object > div.window-manager > div.window-manager-start {
text-align: start;
}
div.window-object
> div.window-manager
> div.window-manager-start
> span.window-manager-label {
display: inline-block;
height: 100%;
width: 100%;
line-height: 1.5em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
div.window-object > div.window-manager > div.window-manager-end {
text-align: end;
}
div.window-object > div.window-manager > div > button {
height: 1.5em;
width: 1.5em;
padding: 0;
margin: 0;
margin-inline-start: 0.25em;
box-sizing: border-box;
border: var(--border-style);
background-color: var(--accent-color);
color: var(--accent-color-fg);
font-family: "Material Symbols Outlined";
text-align: center;
vertical-align: middle;
font-size: 1em;
line-height: 0.5em;
}
div.window-object > iframe.window-content {
height: 100%;
width: 100%;
border: none;
}