mirror of
https://github.com/RGBCube/Site
synced 2025-07-31 13:07:46 +00:00
treewide: site redesign
This commit is contained in:
parent
3d663619e6
commit
4b146bbe4e
80 changed files with 1842 additions and 1117 deletions
|
@ -2,13 +2,4 @@
|
|||
layout: text.vto
|
||||
---
|
||||
|
||||
<div class="content">{{ content }}</div>
|
||||
|
||||
{{ if tags.length !== 0 }}
|
||||
<p>Tags: {{ tags.join(", ") }}</p>
|
||||
{{ /if }}
|
||||
|
||||
<p>
|
||||
Also, if you are a dinosaur that enjoys good technology, check out my
|
||||
<a href="/blog.rss">RSS Feed</a>.
|
||||
</p>
|
||||
<div class="article-content">{{ content }}</div>
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0{{ viewportFixed ? ', maximum-scale=1.0, user-scalable=0' : '' }}">
|
||||
<meta name="darkreader-lock">
|
||||
|
||||
<title>{{ title }}</title>
|
||||
<meta property="og:title" content="{{ title }}">
|
||||
<meta property="og:site_name" content="{{ title }}">
|
||||
<meta name="twitter:title" content="{{ title }}">
|
||||
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta property="og:description" content="{{ description }}">
|
||||
<meta name="twitter:description" content="{{ description }}">
|
||||
|
||||
<meta name="theme-color" content="{{ color }}">
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" href="/blog.rss">
|
||||
<link rel="alternate" type="application/feed+json" href="/blog.json">
|
||||
|
||||
<meta property="og:type" content="{{ type }}">
|
||||
<meta property="og:locale" content="en">
|
||||
|
||||
<link rel="canonical" href="{{ url |> url(true) }}">
|
||||
<meta property="og:url" content="{{ url |> url(true) }}">
|
||||
<meta name="twitter:url" content="{{ url |> url(true) }}">
|
||||
|
||||
<meta name="author" content="RGBCube">
|
||||
<meta property="og:article:author" content="RGBCube">
|
||||
<meta name="twitter:creator" content="HSVSphere">
|
||||
|
||||
{{ if tags.length !== 0 }}
|
||||
<meta name="keywords" content="{{ tags.join(', ') }}">
|
||||
<meta property="og:article:tag" content="{{ tags.join(', ') }}">
|
||||
{{ /if }}
|
||||
|
||||
{{ if date }} <!-- For some reason this is always true, even though I do not set it. -->
|
||||
<meta property="og:article:published_time" content="{{ date }}">
|
||||
{{ /if }}
|
||||
|
||||
{{ if thumbnail }}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="{{ thumbnail }}">
|
||||
<meta property="og:image" content="{{ thumbnail }}">
|
||||
{{ else }}
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:image" content="/assets/icon.webp">
|
||||
{{ /if }}
|
||||
|
||||
<link rel="icon" href="/assets/icon.gif">
|
||||
<link rel="apple-touch-icon" href="/assets/icon.webp">
|
||||
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/BaiJamjureeMedium.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/BaiJamjureeMediumItalic.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/BaiJamjureeBold.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/BaiJamjureeBoldItalic.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/JetBrainsMonoMedium.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/JetBrainsMonoMediumItalic.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/JetBrainsMonoBold.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/woff2" href="/assets/JetBrainsMonoBoldItalic.woff2" crossorigin>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Bai Jamjuree";
|
||||
font-weight: normal;
|
||||
src: url("/assets/BaiJamjureeMedium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Bai Jamjuree";
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
src: url("/assets/BaiJamjureeMediumItalic.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Bai Jamjuree";
|
||||
font-weight: bold;
|
||||
src: url("/assets/BaiJamjureeBold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Bai Jamjuree";
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
src: url("/assets/BaiJamjureeBoldItalic.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
font-weight: normal;
|
||||
src: url("/assets/JetBrainsMonoMedium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
src: url("/assets/JetBrainsMonoMediumItalic.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
font-weight: bold;
|
||||
src: url("/assets/JetBrainsMonoBold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
src: url("/assets/JetBrainsMonoBoldItalic.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: black;
|
||||
--foreground: white;
|
||||
--link: yellow;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--background: white;
|
||||
--foreground: black;
|
||||
--link: red;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Bai Jamjuree";
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: "JetBrains Mono";
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: var(--background);
|
||||
color: var(--foreground);
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration-line: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
{{ content }}
|
||||
</body>
|
||||
</html>
|
|
@ -1,117 +0,0 @@
|
|||
interface CubeProps {
|
||||
front?: Node;
|
||||
back?: Node;
|
||||
left?: Node;
|
||||
right?: Node;
|
||||
top?: Node;
|
||||
bottom?: Node;
|
||||
}
|
||||
|
||||
const Cube = (props: CubeProps) => (
|
||||
<>
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
html {
|
||||
font-size: min(9vw, 9vh, 4.5rem);
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.frame {
|
||||
background-color: white;
|
||||
|
||||
width: min-content;
|
||||
|
||||
padding: 0 0.3rem;
|
||||
border-radius: 1rem;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.frame:hover {
|
||||
background-color: var(--link);
|
||||
}
|
||||
|
||||
.scene {
|
||||
height: 100dvh;
|
||||
width: 100dvw;
|
||||
perspective: 15rem;
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cube {
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
|
||||
position: relative;
|
||||
|
||||
transform: translateZ(-calc(2.5rem - 1px));
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.face {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.front {
|
||||
transform: rotateY(0deg) translateZ(calc(2.5rem - 1px));
|
||||
}
|
||||
|
||||
.top {
|
||||
/* Guess what? Yeah, you guessed right. Safari can't render shit. */
|
||||
transform: rotateX(89.99999999999999deg) translateZ(calc(2.5rem - 1px));
|
||||
}
|
||||
|
||||
.back {
|
||||
transform: rotateY(180deg) translateZ(calc(2.5rem - 1px));
|
||||
}
|
||||
|
||||
.bottom {
|
||||
transform: rotateX(-89.99999999999999deg) translateZ(calc(2.5rem - 1px));
|
||||
}
|
||||
|
||||
.right {
|
||||
transform: rotateY(89.99999999999999deg) translateZ(calc(2.5rem - 1px));
|
||||
}
|
||||
|
||||
.left {
|
||||
transform: rotateY(-89.99999999999999deg) translateZ(calc(2.5rem - 1px));
|
||||
}
|
||||
`,
|
||||
}}
|
||||
>
|
||||
</style>
|
||||
|
||||
<div className="scene">
|
||||
<div className="cube">
|
||||
<div className="face front">{props.front}</div>
|
||||
<div className="face back">{props.back}</div>
|
||||
<div className="face left">{props.left}</div>
|
||||
<div className="face right">{props.right}</div>
|
||||
<div className="face top">{props.top}</div>
|
||||
<div className="face bottom">{props.bottom}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/cube.js"></script>
|
||||
</>
|
||||
);
|
||||
|
||||
export default Cube;
|
259
site/_includes/cube.vto
Normal file
259
site/_includes/cube.vto
Normal file
|
@ -0,0 +1,259 @@
|
|||
<style>
|
||||
:root {
|
||||
--cube-width: 5rem;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: min(9vw, 9vh, 4.5rem);
|
||||
}
|
||||
|
||||
/* Guess what? Yeah, you guessed right. Safari can't render shit. */
|
||||
.cube-face-front { transform: rotateY(0deg) translateZ(calc(var(--cube-width) / 2 - 1px)); }
|
||||
.cube-face-top { transform: rotateX( 89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); }
|
||||
.cube-face-back { transform: rotateY(180deg) translateZ(calc(var(--cube-width) / 2 - 1px)); }
|
||||
.cube-face-bottom { transform: rotateX(-89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); }
|
||||
.cube-face-right { transform: rotateY( 89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); }
|
||||
.cube-face-left { transform: rotateY(-89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); }
|
||||
</style>
|
||||
|
||||
<cube-scene class="
|
||||
h-dvh w-dvw
|
||||
overscroll-none
|
||||
flex items-center justify-center
|
||||
perspective-[calc(var(--cube-width)*3)]
|
||||
">
|
||||
<cube-itself class="
|
||||
size-(--cube-width)
|
||||
transform-3d transform-[translateZ(-calc(var(--cube-width)/2-1px))]
|
||||
">
|
||||
{{> const style = `
|
||||
size-(--cube-width) absolute
|
||||
flex items-center justify-center
|
||||
` }}
|
||||
<cube-face draggable="false" class="{{ style }} cube-face-front"> {{ cube_face_front }} </cube-face>
|
||||
<cube-face draggable="false" class="{{ style }} cube-face-back"> {{ cube_face_back }} </cube-face>
|
||||
<cube-face draggable="false" class="{{ style }} cube-face-left"> {{ cube_face_left }} </cube-face>
|
||||
<cube-face draggable="false" class="{{ style }} cube-face-right"> {{ cube_face_right }} </cube-face>
|
||||
<cube-face draggable="false" class="{{ style }} cube-face-top"> {{ cube_face_top }} </cube-face>
|
||||
<cube-face draggable="false" class="{{ style }} cube-face-bottom"> {{ cube_face_bottom }} </cube-face>
|
||||
</cube-itself>
|
||||
</cube-scene>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const Vec = (x, y, z) => ({
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
|
||||
length() {
|
||||
return Math.sqrt(this.x ** 2 + this.y ** 2 + this.z ** 2);
|
||||
},
|
||||
|
||||
scale(factor) {
|
||||
return Vec(
|
||||
this.x * factor,
|
||||
this.y * factor,
|
||||
this.z * factor,
|
||||
);
|
||||
},
|
||||
|
||||
normalize() {
|
||||
let length = this.length();
|
||||
length = length == 0 ? 1 : length;
|
||||
|
||||
return Vec(
|
||||
this.x / length,
|
||||
this.y / length,
|
||||
this.z / length,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Vec.ZERO = Vec(0, 0, 0);
|
||||
|
||||
Vec.sum = (a, b) => Vec(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z,
|
||||
);
|
||||
|
||||
Vec.sub = (a, b) => Vec(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z,
|
||||
);
|
||||
|
||||
const Quat = (x, y, z, w) => ({
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
w,
|
||||
});
|
||||
|
||||
Quat.fromAxis = (axis) => {
|
||||
const angle = axis.length();
|
||||
|
||||
axis = axis.normalize();
|
||||
|
||||
const half = angle / 2;
|
||||
|
||||
const sinHalf = Math.sin(half);
|
||||
const cosHalf = Math.cos(half);
|
||||
|
||||
const x = axis.x * sinHalf;
|
||||
const y = axis.y * sinHalf;
|
||||
const z = axis.z * sinHalf;
|
||||
const w = cosHalf;
|
||||
|
||||
return Quat(x, y, z, w);
|
||||
};
|
||||
|
||||
Quat.mul = (a, b) => Quat(
|
||||
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
||||
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
||||
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
||||
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
|
||||
);
|
||||
|
||||
const friction = 3;
|
||||
const sensitivity = 0.01;
|
||||
|
||||
// One minute.
|
||||
const screensaverTimeoutMs = 1 * 60 * 1000;
|
||||
|
||||
const mouse = {
|
||||
down: false,
|
||||
lastMove: -screensaverTimeoutMs,
|
||||
previous: Vec.ZERO,
|
||||
};
|
||||
|
||||
const orient = {
|
||||
element: document.querySelector("cube-itself"),
|
||||
quat: Quat(0, 0, 0, 1),
|
||||
|
||||
set(q) {
|
||||
this.quat = q;
|
||||
|
||||
this.element.style.transform =
|
||||
`rotate3d(${q.x}, ${q.y}, ${q.z}, ${Math.acos(q.w) * 2}rad)`;
|
||||
},
|
||||
|
||||
get() {
|
||||
return this.quat;
|
||||
},
|
||||
};
|
||||
|
||||
let velocity = Vec.ZERO;
|
||||
let impulseThisFrame = Vec.ZERO;
|
||||
|
||||
const handleUp = () => {
|
||||
mouse.down = false;
|
||||
};
|
||||
|
||||
document.addEventListener("mouseup", handleUp);
|
||||
document.addEventListener("touchend", handleUp);
|
||||
|
||||
const handleDown = (event) => {
|
||||
mouse.down = true;
|
||||
|
||||
velocity = Vec.ZERO;
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleDown);
|
||||
document.addEventListener("touchstart", handleDown);
|
||||
|
||||
const handleMove = (event) => {
|
||||
// Disables scrolling.
|
||||
event.preventDefault();
|
||||
|
||||
if (!mouse.down) return;
|
||||
|
||||
const newMouse = Vec(event.clientX, event.clientY, 0);
|
||||
|
||||
const timeDelta = (globalThis.performance.now() - mouse.lastMove) / 1000;
|
||||
|
||||
if (timeDelta > 0.1) {
|
||||
// This is a fresh scroll.
|
||||
mouse.previous = newMouse;
|
||||
}
|
||||
|
||||
const delta = Vec.sub(newMouse, mouse.previous);
|
||||
|
||||
mouse.previous = newMouse;
|
||||
mouse.lastMove = globalThis.performance.now();
|
||||
|
||||
const axis = Vec(-delta.y, delta.x, 0)
|
||||
.normalize()
|
||||
.scale(delta.length() * sensitivity);
|
||||
|
||||
impulseThisFrame = Vec.sum(impulseThisFrame, axis);
|
||||
|
||||
const rotation = Quat.fromAxis(axis);
|
||||
|
||||
orient.set(Quat.mul(rotation, orient.get()));
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", handleMove);
|
||||
document.addEventListener("touchmove", (event) => {
|
||||
const delta = event.changedTouches[0];
|
||||
|
||||
event.clientX = delta.clientX;
|
||||
event.clientY = delta.clientY;
|
||||
|
||||
handleMove(event);
|
||||
});
|
||||
|
||||
let lastUpdate = 0;
|
||||
|
||||
const updateFrame = (timestamp) => {
|
||||
if (lastUpdate == 0) lastUpdate = timestamp;
|
||||
|
||||
const delta = (timestamp - lastUpdate) / 1000;
|
||||
lastUpdate = timestamp;
|
||||
|
||||
if (mouse.down) {
|
||||
velocity = impulseThisFrame.scale(1 / delta);
|
||||
impulseThisFrame = Vec.ZERO;
|
||||
|
||||
requestAnimationFrame(updateFrame);
|
||||
return;
|
||||
}
|
||||
|
||||
const decay = Math.exp(-delta * friction);
|
||||
|
||||
const effectiveDelta = friction > 0 ? (1 - decay) / friction : delta;
|
||||
|
||||
let theta = effectiveDelta * velocity.length();
|
||||
|
||||
velocity.x *= decay;
|
||||
velocity.y *= decay;
|
||||
velocity.z *= decay;
|
||||
|
||||
if (friction > 0 && velocity.length() < 0.00001) {
|
||||
theta += velocity.length() / friction;
|
||||
|
||||
velocity.x = 0;
|
||||
velocity.y = 0;
|
||||
velocity.z = 0;
|
||||
}
|
||||
|
||||
if (globalThis.performance.now() - mouse.lastMove > screensaverTimeoutMs) {
|
||||
const impulse = Vec(0.7, 0.7, -0.7);
|
||||
velocity = Vec.sum(impulse.scale(effectiveDelta * 3), velocity);
|
||||
}
|
||||
|
||||
const axis = Vec(velocity.x, velocity.y, velocity.z)
|
||||
.normalize()
|
||||
.scale(theta);
|
||||
|
||||
const rotation = Quat.fromAxis(axis);
|
||||
|
||||
orient.set(Quat.mul(rotation, orient.get()));
|
||||
|
||||
requestAnimationFrame(updateFrame);
|
||||
};
|
||||
|
||||
updateFrame(0);
|
||||
</script>
|
63
site/_includes/default.vto
Normal file
63
site/_includes/default.vto
Normal file
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
layout: null
|
||||
---
|
||||
|
||||
<head>
|
||||
<!-- CHARSET -->
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<!-- VIEWPORT -->
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0{{ if prevent_zoom }},maximum-scale=1.0,user-scalable=0{{ /if }}">
|
||||
|
||||
<!-- NUKE DARKREADER -->
|
||||
<meta name="darkreader-lock">
|
||||
|
||||
<!-- META -->
|
||||
{{ if title }} <title>{{ title }}</title> {{ /if }}
|
||||
{{ if description }} <meta name="description" content="{{ description }}"> {{ /if }}
|
||||
{{ if author }} <meta name="author" content="{{ author }}"> {{ /if }}
|
||||
{{ if keywords }} <meta name="keywords" content="{{ keywords.join(",") }}"> {{ /if }}
|
||||
|
||||
<!-- FANCY -->
|
||||
<link rel="icon" href="/assets/icons/icon.gif">
|
||||
<meta name="theme-color" content="{{ color }}">
|
||||
|
||||
<!-- ALTERNATIVE FORMATS -->
|
||||
<link rel="alternate" type="application/rss+xml" href="/blog.rss">
|
||||
<link rel="alternate" type="application/feed+json" href="/blog.json">
|
||||
|
||||
<!-- CANONICAL URL -->
|
||||
<link rel="canonical" href="{{ url |> url(true) }}">
|
||||
|
||||
<!-- OPENGRAPH CONTAINMENT ZONE -->
|
||||
{{ if title }} <meta property="og:title" name="title" content="{{ title }}"> {{ /if }}
|
||||
{{ if description}} <meta property="og:description" content="{{ description }}"> {{ /if }}
|
||||
<meta property="og:type" content="{{ type ?? "website" }}">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta property="og:site_name" content="{{ site_name }}">
|
||||
<meta property="og:url" content="{{ url |> url(true) }}">
|
||||
{{ if thumbnail }}
|
||||
<meta property="og:image" name="image" content="{{ thumbnail |> url(true) }}">
|
||||
{{ else }}
|
||||
<meta property="og:image" name="image" content="{{ "/assets/icons/icon.webp" |> url(true) }}">
|
||||
{{ /if }}
|
||||
|
||||
<!-- TWITTER CONTAINMENT ZONE -->
|
||||
{{ if title }} <meta name="twitter:title" content="{{ title }}"> {{ /if }}
|
||||
<meta name="twitter:creator" content="HSVSphere">
|
||||
{{ if thumbnail }}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="{{ thumbnail |> url(true) }}">
|
||||
{{ else }}
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:image" content="{{ "/assets/icons/icon.webp" |> url(true) }}">
|
||||
{{ /if }}
|
||||
|
||||
<!-- STYLES -->
|
||||
<link href="/assets/css/fonts.css" rel="stylesheet" inline>
|
||||
<link href="/assets/css/default.css" rel="stylesheet" inline>
|
||||
</head>
|
||||
|
||||
<body class="bg-white text-black dark:bg-black dark:text-white">
|
||||
{{ content }}
|
||||
</body>
|
|
@ -1,196 +1,47 @@
|
|||
---
|
||||
layout: base.vto
|
||||
layout: default.vto
|
||||
---
|
||||
|
||||
<style>
|
||||
/* Centering */
|
||||
html,
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
<div class="flex justify-center">
|
||||
<div class="block text-xl w-[min(100vw,50rem)]">
|
||||
|
||||
.centered {
|
||||
font-size: large;
|
||||
<nav class="
|
||||
transform-[rotateX(180deg)]
|
||||
text-black text-2xl font-bold
|
||||
absolute w-[inherit] z-999
|
||||
overflow-x-auto
|
||||
">
|
||||
<div class="transform-[rotateX(180deg)] flex items-start justify-evenly">
|
||||
{{> const style = `
|
||||
flex-auto text-center px-3 pt-3 pb-2
|
||||
bg-white font-[ecrou] border-2 border-black
|
||||
origin-bottom
|
||||
md:hover:text-[300%] ease-[cubic-bezier(1,-0.07,0.57,1.56)] md:duration-200
|
||||
|
||||
display: initial;
|
||||
width: min(100vw - 2rem, 50rem);
|
||||
}
|
||||
md:hover:not-active:after:content-['?']
|
||||
md:active:after:content-['!!'] active:after:italic
|
||||
|
||||
/* Make wrapping pretty */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
/* Rotate nav, tables and codeblocks so the scrollbar is at the top */
|
||||
.rotated {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
|
||||
/* Make tables and other elements scroll horizontally */
|
||||
.overflow {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* Style nav */
|
||||
nav {
|
||||
font-size: larger;
|
||||
word-break: normal;
|
||||
|
||||
overflow-wrap: normal;
|
||||
overflow-x: auto;
|
||||
|
||||
background-color: white;
|
||||
padding: 0.3rem 0.6rem;
|
||||
|
||||
/* Rotated 180deg so it's top instead of bottom. */
|
||||
border-top-left-radius: 1rem;
|
||||
border-top-right-radius: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
nav {
|
||||
border: 0.15rem solid black;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
|
||||
/* Make images fit */
|
||||
p:has(img) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Style content */
|
||||
.content {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 0.15rem solid var(--foreground);
|
||||
|
||||
padding: 0.05rem 1rem;
|
||||
margin: 1.3rem 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
blockquote {
|
||||
background-color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
blockquote {
|
||||
background-color: #EEE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Style tables */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Doesn't seem to work with the scrolling? */
|
||||
/* thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
} */
|
||||
|
||||
th, td {
|
||||
border: 0.15rem solid var(--foreground);
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--foreground);
|
||||
color: var(--background);
|
||||
}
|
||||
|
||||
/* Codeblocks */
|
||||
.rotated:has(pre) {
|
||||
padding: 0.6rem;
|
||||
border: 0.15rem solid var(--foreground);
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.hljs-attr { color: lightblue; }
|
||||
.hljs-built_in { color: firebrick; }
|
||||
.hljs-keyword { color: firebrick; }
|
||||
.hljs-number { color: mediumslateblue; }
|
||||
.hljs-string { color: limegreen; }
|
||||
.hljs-title { color: lightcoral; }
|
||||
.hljs-type { color: aquamarine; }
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.hljs-attr { color: darkblue; }
|
||||
.hljs-built_in { color: darkred; }
|
||||
.hljs-keyword { color: darkred; }
|
||||
.hljs-number { color: darkslateblue; }
|
||||
.hljs-string { color: darkgreen; }
|
||||
.hljs-title { color: darkgoldenrod; }
|
||||
.hljs-type { color: darkcyan; }
|
||||
}
|
||||
|
||||
/* The end */
|
||||
hr {
|
||||
border: 0.15rem solid var(--foreground);
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
font-size: small;
|
||||
|
||||
margin-bottom: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="centered">
|
||||
<nav class="rotated">
|
||||
<div class="rotated">
|
||||
<a href="/">HOME</a>
|
||||
<a href="/about">ABOUT</a>
|
||||
<a href="/blog">BLOG</a>
|
||||
<a href="/contact">CONTACT</a>
|
||||
</div>
|
||||
</nav>
|
||||
nuclear
|
||||
` }}
|
||||
<style>.right-delete:has(+ *:hover) { border-right-width: 0; }</style>
|
||||
<a class="nav-link {{ style }} right-delete" href="/">home</a>
|
||||
<a class="nav-link {{ style }} right-delete not-hover:border-l-0" href="/about/">about</a>
|
||||
<a class="nav-link {{ style }} right-delete not-hover:border-l-0" href="/blog/">blog</a>
|
||||
<a class="nav-link {{ style }} not-hover:border-l-0" href="/contact/">contact</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{> const padding = 4 }} <!-- Keep in sync with -mx-N in default.css. -->
|
||||
<div class="text-content p-{{ padding }} pt-{{ padding + 12 }}">
|
||||
{{ content }}
|
||||
|
||||
<hr>
|
||||
<footer>Copyright {{ new Date().getFullYear() }} © RGBCube</footer>
|
||||
</div>
|
||||
|
||||
<hr class="border-2 border-black dark:border-white">
|
||||
|
||||
<footer class="flex justify-center text-sm pt-1 pb-2 wrap-anywhere">
|
||||
Copyright {{ Temporal.Now.plainDateISO().year }} ©
|
||||
<a href="/"><img class="pl-0.5 pt-0.25 size-5" src="/assets/icons/icon.gif" alt="RGBCube"></a>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue