1
Fork 0
mirror of https://github.com/RGBCube/Site synced 2025-08-02 14:07:47 +00:00

Compare commits

...

28 commits

Author SHA1 Message Date
e22ca700ee
default.vto: add spaces to width header 2025-06-03 05:16:25 +03:00
ea4f34fc95
cube: decrease timeout to 15sec 2025-06-03 05:16:16 +03:00
c1e7afe666
text: always put footer at bottom 2025-06-03 05:13:23 +03:00
879843620a
text: remove unnecessary attribute 2025-06-03 04:56:18 +03:00
78415c1da1
text: adjust footer 2025-06-03 04:46:04 +03:00
9ec127293a
css: make html and body occupy fullscreen 2025-06-03 04:41:32 +03:00
490886b5f4
text: draggable=false on tiny cube 2025-06-03 04:25:23 +03:00
5c35c3b73d
cube: make script operators methods 2025-06-03 04:19:21 +03:00
2ccbbf3a17
cube: make impulseIdle a global 2025-06-03 04:08:55 +03:00
217f19841f
text: replace cube gif with real cube 2025-06-03 04:02:22 +03:00
0ffe6c39b1
cube: multicube support 2025-06-03 03:51:00 +03:00
23bdfef6cc
cube: unfix size 2025-06-03 03:35:35 +03:00
e25d198cc9
cube: make cube size a template parameter 2025-06-03 03:32:45 +03:00
adeca4879a
cube: separate rgbcube logic 2025-06-03 03:23:20 +03:00
b6c3592c2a
cube: make more generic, step 1 2025-06-03 03:20:09 +03:00
70f94588c9
blog: fix disappearing 2025-06-03 03:14:14 +03:00
f889ca595b
blog: fix xFactor 2025-06-03 03:10:03 +03:00
638cfff96b
Revert "cube: make cube-face a class"
This reverts commit dcca24e7cc.
2025-06-03 03:04:21 +03:00
dcca24e7cc
cube: make cube-face a class 2025-06-03 02:56:14 +03:00
1bb9d9c541
css: hide border of <code> links when active too 2025-06-03 02:53:01 +03:00
3732704d74
blog: fix animation 2025-06-03 02:46:09 +03:00
5efa81ca2a
blog: ???? 2025-06-03 01:57:45 +03:00
6564bd61c7
blog: ??? 2025-06-03 00:45:35 +03:00
ab09647f76
code: better link styling 2025-06-02 23:46:22 +03:00
c96c59ecf5
headers: make them actually have unique IDs 2025-06-02 23:20:57 +03:00
e799cc1c45
headers: make anchors truly unique 2025-06-02 23:18:49 +03:00
a01188e892
headers: fix generated links 2025-06-02 23:07:49 +03:00
99c21c01c7
headers: link it up 2025-06-02 23:04:43 +03:00
11 changed files with 454 additions and 168 deletions

2
deno.lock generated
View file

@ -666,7 +666,6 @@
"https://deno.land/x/lume@v3.0.2/deps/hex.ts": "828718f24a780ff3ade8d0a8a5b57497cb31c257560ef12af99b6eb1a31e3bbd", "https://deno.land/x/lume@v3.0.2/deps/hex.ts": "828718f24a780ff3ade8d0a8a5b57497cb31c257560ef12af99b6eb1a31e3bbd",
"https://deno.land/x/lume@v3.0.2/deps/highlight.ts": "e8f830a1137ff7e8246ce21518452b8cbf8089db409458c6d9c31040c11d8428", "https://deno.land/x/lume@v3.0.2/deps/highlight.ts": "e8f830a1137ff7e8246ce21518452b8cbf8089db409458c6d9c31040c11d8428",
"https://deno.land/x/lume@v3.0.2/deps/http.ts": "6d9add7c6fe0c0381050aa773ae8590166ccc84c5115d2cde271320c315a110d", "https://deno.land/x/lume@v3.0.2/deps/http.ts": "6d9add7c6fe0c0381050aa773ae8590166ccc84c5115d2cde271320c315a110d",
"https://deno.land/x/lume@v3.0.2/deps/icons.ts": "4379e1443d982ab4f85237342d165ae54c981cdb7e06480c222d208999e21f15",
"https://deno.land/x/lume@v3.0.2/deps/init.ts": "05d45af66ebdfe63e43540618f51ece8f99d98dc49de890f10eeb43abe9ed0f3", "https://deno.land/x/lume@v3.0.2/deps/init.ts": "05d45af66ebdfe63e43540618f51ece8f99d98dc49de890f10eeb43abe9ed0f3",
"https://deno.land/x/lume@v3.0.2/deps/jsonc.ts": "79f0eddc3c9e593310eb8e5918eb1506b1c7d7816e4ecb96894f634ecbe626ff", "https://deno.land/x/lume@v3.0.2/deps/jsonc.ts": "79f0eddc3c9e593310eb8e5918eb1506b1c7d7816e4ecb96894f634ecbe626ff",
"https://deno.land/x/lume@v3.0.2/deps/lightningcss.ts": "5f5167c6eb306ef759f0043f8f33f2eaf63c69210aa1aa837505e990ee619c46", "https://deno.land/x/lume@v3.0.2/deps/lightningcss.ts": "5f5167c6eb306ef759f0043f8f33f2eaf63c69210aa1aa837505e990ee619c46",
@ -692,7 +691,6 @@
"https://deno.land/x/lume@v3.0.2/plugins/code_highlight.ts": "ac6327e688e9e8fbd7798bdcc5f76b46d27db3e22ea3b74f545dc3296e8a1261", "https://deno.land/x/lume@v3.0.2/plugins/code_highlight.ts": "ac6327e688e9e8fbd7798bdcc5f76b46d27db3e22ea3b74f545dc3296e8a1261",
"https://deno.land/x/lume@v3.0.2/plugins/extract_date.ts": "38af8e5960d66a74a72977eb19521da4353ab32d3941e97c1526aa3b91175a9e", "https://deno.land/x/lume@v3.0.2/plugins/extract_date.ts": "38af8e5960d66a74a72977eb19521da4353ab32d3941e97c1526aa3b91175a9e",
"https://deno.land/x/lume@v3.0.2/plugins/feed.ts": "b07aed4cda270cfaacb26f9974dbb962f936dbfde4d770c53e478e4682c791e1", "https://deno.land/x/lume@v3.0.2/plugins/feed.ts": "b07aed4cda270cfaacb26f9974dbb962f936dbfde4d770c53e478e4682c791e1",
"https://deno.land/x/lume@v3.0.2/plugins/icons.ts": "c69428254024d694eca34f9b5c4888aedf83d2d9b38f860533952c28cc814333",
"https://deno.land/x/lume@v3.0.2/plugins/inline.ts": "737d7de09d196476b55ecbe7ddb0e651ba2d5d39ca5a418cb15ff48e124907c1", "https://deno.land/x/lume@v3.0.2/plugins/inline.ts": "737d7de09d196476b55ecbe7ddb0e651ba2d5d39ca5a418cb15ff48e124907c1",
"https://deno.land/x/lume@v3.0.2/plugins/json.ts": "5c49499e56b919ec848d4118ec97dd4fe0a323a6cc4c648dc45ab55297614c12", "https://deno.land/x/lume@v3.0.2/plugins/json.ts": "5c49499e56b919ec848d4118ec97dd4fe0a323a6cc4c648dc45ab55297614c12",
"https://deno.land/x/lume@v3.0.2/plugins/lightningcss.ts": "6b5236cc78c1ae4af5b4a0037a0345797381f5043c667ae1ddeb4f67e445c7c4", "https://deno.land/x/lume@v3.0.2/plugins/lightningcss.ts": "6b5236cc78c1ae4af5b4a0037a0345797381f5043c667ae1ddeb4f67e445c7c4",

76
site.ts
View file

@ -1,25 +1,25 @@
import lume from "lume/mod.ts"; import lume from "lume/mod.ts";
import extract_date from "lume/plugins/extract_date.ts"; import extractDate from "lume/plugins/extract_date.ts";
import code_highlight from "lume/plugins/code_highlight.ts"; import codeHighlight from "lume/plugins/code_highlight.ts";
import redirects from "lume/plugins/redirects.ts"; import redirects from "lume/plugins/redirects.ts";
import tailwindcss from "lume/plugins/tailwindcss.ts"; import tailwindcss from "lume/plugins/tailwindcss.ts";
import lightningcss from "lume/plugins/lightningcss.ts"; import lightningcss from "lume/plugins/lightningcss.ts";
import resolve_urls from "lume/plugins/resolve_urls.ts"; import resolveUrls from "lume/plugins/resolve_urls.ts";
import slugify_urls from "lume/plugins/slugify_urls.ts"; import slugifyUrls from "lume/plugins/slugify_urls.ts";
import check_urls from "lume/plugins/check_urls.ts"; import checkUrls from "lume/plugins/check_urls.ts";
import inline from "lume/plugins/inline.ts"; import inline from "lume/plugins/inline.ts";
import feed from "lume/plugins/feed.ts"; import feed from "lume/plugins/feed.ts";
import sitemap from "lume/plugins/sitemap.ts"; import sitemap from "lume/plugins/sitemap.ts";
import minify_html from "lume/plugins/minify_html.ts"; import minifyHtml from "lume/plugins/minify_html.ts";
const site_name = "RGBCube"; const siteName = "RGBCube";
const site_description = const siteDescription =
"The home directory and journal of RGBCube and his work."; "The home directory and journal of RGBCube and his work.";
const author = "RGBCube"; const author = "RGBCube";
const color = "#00FFFF"; const color = "#00FFFF";
const path_assets = "/assets"; const pathAssets = "/assets";
const site = lume({ const site = lume({
src: "./site", src: "./site",
@ -31,9 +31,9 @@ const site = lume({
site.data("layout", "default.vto"); site.data("layout", "default.vto");
site.data("site_name", site_name); site.data("site_name", siteName);
site.data("title", site_name); site.data("title", siteName);
site.data("description", site_description); site.data("description", siteDescription);
site.data("author", author); site.data("author", author);
site.data("color", color); site.data("color", color);
@ -69,20 +69,52 @@ site.process([".html"], (pages) => {
element.parentNode!.insertBefore(wrapper, element); element.parentNode!.insertBefore(wrapper, element);
wrapper.appendChild(element); wrapper.appendChild(element);
}); });
document
.querySelectorAll(".text-content :where(h1, h2, h3, h4, h5, h6)")
.forEach((header) => {
if (header.id || header.closest("a") || header.querySelector("a")) {
return;
}
const textNormalized = header
.textContent!
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.trim();
let textUnique = textNormalized;
let counter = 1;
while (document.getElementById(textUnique)) {
counter++;
textUnique = `${textNormalized}-${counter}`;
}
header.id = textUnique;
const link = document.createElement("a");
link.setAttribute("href", "#" + textUnique);
header.parentNode!.insertBefore(link, header);
link.appendChild(header);
});
}); });
}); });
site.use(extract_date()); site.use(extractDate());
site.use(redirects()); site.use(redirects());
site.use(tailwindcss()); site.use(tailwindcss());
site.use(code_highlight()); site.use(codeHighlight());
site.use(resolve_urls()); site.use(resolveUrls());
site.use(slugify_urls({ site.use(slugifyUrls({
extensions: "*", extensions: "*",
})); }));
site.use(check_urls({ site.use(checkUrls({
strict: true, strict: true,
throw: true, throw: true,
})); }));
@ -95,12 +127,12 @@ site.use(feed({
limit: Infinity, limit: Infinity,
info: { info: {
title: site_name, title: siteName,
description: site_description, description: siteDescription,
authorName: author, authorName: author,
image: `${path_assets}/icons/icon.webp`, image: `${pathAssets}/icons/icon.webp`,
icon: `${path_assets}/icons/icon.webp`, icon: `${pathAssets}/icons/icon.webp`,
color, color,
@ -122,7 +154,7 @@ site.use(sitemap({
site.use(lightningcss()); // TODO: LightningCSS doesn't handle inline styles. site.use(lightningcss()); // TODO: LightningCSS doesn't handle inline styles.
site.use(inline()); site.use(inline());
site.use(minify_html({ site.use(minifyHtml({
options: { options: {
// TODO: This breaks tailwind. // TODO: This breaks tailwind.
// minify_css: true, // minify_css: true,

View file

@ -4,10 +4,20 @@ prevent_zoom: true
--- ---
<style> <style>
html {
font-size: min(9svw, 9svh, 4.5rem);
overscroll-behavior: none;
}
cube-scene {
height: 100%;
}
cube-face { cube-face {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr);
box-shadow: 0 0 10px var(--foreground); box-shadow: 0 0 10px var(--foreground);
} }
</style> </style>
@ -28,4 +38,6 @@ prevent_zoom: true
{{ set cube_face_top = cube_face }} {{ set cube_face_top = cube_face }}
{{ set cube_face_bottom = cube_face }} {{ set cube_face_bottom = cube_face }}
{{ set cube_size = "5rem" }}
{{ set cube_last = true }}
{{ include "cube.vto" }} {{ include "cube.vto" }}

View file

@ -1,44 +1,43 @@
<style> {{ set cube_minus_px = cube_small ? "" : "- 1px" }}
:root {
--cube-width: 5rem;
}
html { {{ if cube_last }}
font-size: min(9svw, 9svh, 4.5rem); <style>
overscroll-behavior: none; cube-face {
height: {{ cube_size }};
width: {{ cube_size }};
position: absolute;
} }
/* Guess what? Yeah, you guessed right. Safari can't render shit. */ /* 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-front { transform: rotateY(0deg) translateZ(calc({{ cube_size }} / 2 {{ cube_minus_px }})); }
.cube-face-top { transform: rotateX( 89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); } .cube-face-top { transform: rotateX( 89.99999999999999deg) translateZ(calc({{ cube_size }} / 2 {{ cube_minus_px }})); }
.cube-face-back { transform: rotateY(180deg) translateZ(calc(var(--cube-width) / 2 - 1px)); } .cube-face-back { transform: rotateY(180deg) translateZ(calc({{ cube_size }} / 2 {{ cube_minus_px }})); }
.cube-face-bottom { transform: rotateX(-89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); } .cube-face-bottom { transform: rotateX(-89.99999999999999deg) translateZ(calc({{ cube_size }} / 2 {{ cube_minus_px }})); }
.cube-face-right { transform: rotateY( 89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); } .cube-face-right { transform: rotateY( 89.99999999999999deg) translateZ(calc({{ cube_size }} / 2 {{ cube_minus_px }})); }
.cube-face-left { transform: rotateY(-89.99999999999999deg) translateZ(calc(var(--cube-width) / 2 - 1px)); } .cube-face-left { transform: rotateY(-89.99999999999999deg) translateZ(calc({{ cube_size }} / 2 {{ cube_minus_px }})); }
</style> </style>
{{ /if }}
<cube-scene class=" <cube-scene class="
h-dvh w-dvw
flex items-center justify-center flex items-center justify-center
perspective-[calc(var(--cube-width)*3)] perspective-[calc({{ cube_size.replaceAll(" ", "_") }}*3)]
overscroll-none
"> ">
<cube-itself class=" <cube-itself class="
size-(--cube-width) size-[{{ cube_size }}]
transform-3d transform-[translateZ(-calc(var(--cube-width)/2-1px))] transform-3d transform-[translateZ(calc({{ cube_size.replaceAll(" ", "_") }}_/_-2))]
"> ">
{{> const style = ` <cube-face draggable="false" class="cube-face-front"> {{ cube_face_front }} </cube-face>
size-(--cube-width) absolute <cube-face draggable="false" class="cube-face-back"> {{ cube_face_back }} </cube-face>
flex items-center justify-center <cube-face draggable="false" class="cube-face-left"> {{ cube_face_left }} </cube-face>
` }} <cube-face draggable="false" class="cube-face-right"> {{ cube_face_right }} </cube-face>
<cube-face draggable="false" class="{{ style }} cube-face-front"> {{ cube_face_front }} </cube-face> <cube-face draggable="false" class="cube-face-top"> {{ cube_face_top }} </cube-face>
<cube-face draggable="false" class="{{ style }} cube-face-back"> {{ cube_face_back }} </cube-face> <cube-face draggable="false" class="cube-face-bottom"> {{ cube_face_bottom }} </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-itself>
</cube-scene> </cube-scene>
{{ if cube_last }}
<script> <script>
"use strict"; "use strict";
@ -61,27 +60,40 @@
return Vec(this.x / length, this.y / length, this.z / length); return Vec(this.x / length, this.y / length, this.z / length);
}, },
sum(that) {
return Vec(
this.x + that.x,
this.y + that.y,
this.z + that.z,
);
},
sub(that) {
return Vec(
this.x - that.x,
this.y - that.y,
this.z - that.z,
);
},
}); });
Vec.ZERO = Vec(0, 0, 0); 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) => ({ const Quat = (x, y, z, w) => ({
x, x,
y, y,
z, z,
w, w,
mul(that) {
return Quat(
this.w * that.x + this.x * that.w + this.y * that.z - this.z * that.y,
this.w * that.y - this.x * that.z + this.y * that.w + this.z * that.x,
this.w * that.z + this.x * that.y - this.y * that.x + this.z * that.w,
this.w * that.w - this.x * that.x - this.y * that.y - this.z * that.z,
);
}
}); });
Quat.fromAxis = (axis) => { Quat.fromAxis = (axis) => {
@ -102,19 +114,12 @@
return Quat(x, y, z, w); 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 friction = 3;
const sensitivityMouse = 0.01; const sensitivityMouse = 0.01;
const sensitivityWheel = 0.006; const sensitivityWheel = 0.006;
// One minute. // 15 seconds.
const screensaverTimeoutMs = 1 * 60 * 1000; const screensaverTimeoutMs = 15 * 1000;
const mouse = { const mouse = {
down: false, down: false,
@ -123,15 +128,16 @@
}; };
const orient = { const orient = {
element: document.querySelector("cube-itself"), elements: document.querySelectorAll("cube-itself"),
quat: Quat(0, 0, 0, 1), quat: Quat(0, 0, 0, 1),
set(q) { set(q) {
this.quat = q; this.quat = q;
this.element.style.transform = `rotate3d(${q.x}, ${q.y}, ${q.z}, ${ for (const element of this.elements) {
Math.acos(q.w) * 2 element.style.transform =
}rad)`; `rotate3d(${q.x}, ${q.y}, ${q.z}, ${Math.acos(q.w) * 2}rad)`;
}
}, },
get() { get() {
@ -141,6 +147,7 @@
let velocity = Vec.ZERO; let velocity = Vec.ZERO;
let impulseThisFrame = Vec.ZERO; let impulseThisFrame = Vec.ZERO;
let impulseIdle = Vec(2, 2, -2);
const handleUp = () => { const handleUp = () => {
mouse.down = false; mouse.down = false;
@ -171,7 +178,7 @@
mouse.previous = newMouse; mouse.previous = newMouse;
} }
const delta = Vec.sub(newMouse, mouse.previous); const delta = newMouse.sub(mouse.previous);
mouse.previous = newMouse; mouse.previous = newMouse;
mouse.lastMove = globalThis.performance.now(); mouse.lastMove = globalThis.performance.now();
@ -181,10 +188,10 @@
.scale(delta.length()) .scale(delta.length())
.scale(sensitivityMouse); .scale(sensitivityMouse);
impulseThisFrame = Vec.sum(impulseThisFrame, axis); impulseThisFrame = impulseThisFrame.sum(axis);
const rotation = Quat.fromAxis(axis); const rotation = Quat.fromAxis(axis);
orient.set(Quat.mul(rotation, orient.get())); orient.set(rotation.mul(orient.get()));
}; };
document.addEventListener("mousemove", handleMove); document.addEventListener("mousemove", handleMove);
@ -211,10 +218,10 @@
const axis = Vec(event.deltaY, -event.deltaX, 0) const axis = Vec(event.deltaY, -event.deltaX, 0)
.scale(sensitivityWheel); .scale(sensitivityWheel);
impulseThisFrame = Vec.sum(impulseThisFrame, axis); impulseThisFrame = impulseThisFrame.sum(axis);
const rotation = Quat.fromAxis(axis); const rotation = Quat.fromAxis(axis);
orient.set(Quat.mul(rotation, orient.get())); orient.set(rotation.mul(orient.get()));
}; };
document.addEventListener("wheel", handleWheel, { passive: false }); document.addEventListener("wheel", handleWheel, { passive: false });
@ -254,8 +261,7 @@
} }
if (globalThis.performance.now() - mouse.lastMove > screensaverTimeoutMs) { if (globalThis.performance.now() - mouse.lastMove > screensaverTimeoutMs) {
const impulse = Vec(0.7, 0.7, -0.7); velocity = velocity.sum(impulseIdle.scale(effectiveDelta));
velocity = Vec.sum(impulse.scale(effectiveDelta * 3), velocity);
} }
const axis = Vec(velocity.x, velocity.y, velocity.z) const axis = Vec(velocity.x, velocity.y, velocity.z)
@ -264,10 +270,11 @@
const rotation = Quat.fromAxis(axis); const rotation = Quat.fromAxis(axis);
orient.set(Quat.mul(rotation, orient.get())); orient.set(rotation.mul(orient.get()));
requestAnimationFrame(updateFrame); requestAnimationFrame(updateFrame);
}; };
updateFrame(0); updateFrame(0);
</script> </script>
{{ /if }}

View file

@ -0,0 +1,72 @@
<style>
cube-face {
display: flex;
&::after {
z-index: -1;
content: "";
height: inherit;
width: inherit;
position: absolute;
}
}
.cube-face-front {
background: linear-gradient(to bottom, cyan, blue);
&::after {
background: linear-gradient(to bottom, white, magenta);
mask-image: linear-gradient(to left, magenta, transparent);
}
}
.cube-face-top {
background: linear-gradient(to bottom, lime, cyan);
&::after {
background: linear-gradient(to bottom, yellow, white);
mask-image: linear-gradient(to left, white, transparent);
}
}
.cube-face-back {
background: linear-gradient(to bottom, yellow, red);
&::after {
background: linear-gradient(to bottom, lime, black);
mask-image: linear-gradient(to left, black, transparent);
}
}
.cube-face-bottom {
background: linear-gradient(to bottom, blue, black);
&::after {
background: linear-gradient(to bottom, magenta, red);
mask-image: linear-gradient(to left, red, transparent);
}
}
.cube-face-right {
background: linear-gradient(to bottom, white, magenta);
&::after {
background: linear-gradient(to bottom, yellow, red);
mask-image: linear-gradient(to left, red, transparent);
}
}
.cube-face-left {
background: linear-gradient(to bottom, lime, black);
&::after {
background: linear-gradient(to bottom, cyan, blue);
mask-image: linear-gradient(to left, blue, transparent);
}
}
</style>
{{ include "cube.vto" }}

View file

@ -2,8 +2,8 @@
layout: default.vto layout: default.vto
--- ---
<div class="flex justify-center"> <div class="flex justify-center h-[inherit]">
<div class="block text-xl w-[min(100vw,50rem)]"> <div class="flex flex-col h-[inherit] w-[min(100dvw,50rem)]">
<nav class=" <nav class="
transform-[rotateX(180deg)] transform-[rotateX(180deg)]
@ -28,16 +28,20 @@ layout: default.vto
</div> </div>
</nav> </nav>
{{> const padding = 4 }} <!-- Keep in sync with -mx-N in default.css. --> {{> const padding = 4 }}
<div class="text-content p-{{ padding }} pt-{{ padding + 12 }}"> <div class="text-content text-xl p-{{ padding }} pt-{{ padding + 12 }}">
{{ content }} {{ content }}
</div> </div>
<hr class="border-2 border-black dark:border-white"> <footer class="flex justify-center text-sm wrap-anywhere mt-auto p-1.5 pb-2 border-t-4 border-x-4">
<footer class="flex justify-center text-sm pt-1 pb-2 wrap-anywhere">
Copyright {{ Temporal.Now.plainDateISO().year }} © 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>
<a draggable="false" class="flex items-center pl-2" href="/">
{{ set cube_size = "0.75rem" }}
{{ set cube_small = true }}
{{ set cube_last = true }}
{{ include "rgbcube.vto" }}
</a>
</footer> </footer>
</div> </div>

View file

@ -125,6 +125,11 @@
} }
} }
html, body {
height: 100dvh;
width: 100dvw;
}
.text-content { .text-content {
@apply space-y-3; @apply space-y-3;
@ -160,13 +165,28 @@
} }
a { a {
@apply inline-block wrap-anywhere text-[red] dark:text-[yellow] px-1 pb-0.75 @apply m-0;
border-2 border-[transparent] border-dashed;
* { * {
@apply wrap-anywhere; @apply wrap-anywhere;
} }
&:not(:has(> code:only-child)) {
@apply px-1;
&:not(.font-mono) {
@apply pb-0.75;
}
}
&:not(:has(h1, h2, h3, h4, h5, h6)) {
@apply inline-block wrap-anywhere text-[red] dark:text-[yellow] border-2
border-[transparent] border-dashed;
&:has(> code:only-child) {
@apply border-dotted;
}
&:hover { &:hover {
@apply border-[red] dark:border-[yellow]; @apply border-[red] dark:border-[yellow];
} }
@ -176,6 +196,19 @@
} }
} }
& :where(h1, h2, h3, h4, h5, h6) {
@apply before:underline before:underline-offset-4;
* {
@apply inline-block;
}
&:hover::before {
@apply italic text-[red] dark:text-[yellow];
}
}
}
/* TODO: Make it better. */ /* TODO: Make it better. */
table { table {
@apply border-collapse whitespace-nowrap; @apply border-collapse whitespace-nowrap;
@ -194,12 +227,20 @@
@apply border-black dark:border-white; @apply border-black dark:border-white;
} }
code:not(pre > code) {
@apply border-1 border-dotted px-2 py-0.5 border-black dark:border-white;
a:hover &, a:active & {
@apply border-transparent;
}
}
pre code, pre code * { pre code, pre code * {
@apply whitespace-pre; @apply whitespace-pre;
} }
div:has(> pre code) { div:has(> pre code) {
@apply outline-1 outline-dotted outline-offset-1 outline-[#444] border-1 @apply outline outline-dotted outline-offset-1 outline-[#444] border-1
border-black p-2 bg-[#eee] dark:outline-[#bbb] dark:border-white border-black p-2 bg-[#eee] dark:outline-[#bbb] dark:border-white
dark:bg-[#111]; dark:bg-[#111];
} }
@ -220,8 +261,7 @@
} }
hr { hr {
@apply border-1 border-black dark:border-white @apply border-1 border-black dark:border-white;
-mx-4; /* Keep in sync with p-N in text.vto. */
} }
blockquote { blockquote {

View file

@ -9,8 +9,8 @@ title: about:blog
Blog Articles Blog Articles
<span class="whitespace-nowrap overflow-hidden text-sm font-[ecrou]"> <span class="whitespace-nowrap overflow-hidden text-sm font-[ecrou]">
<a href="/blog.rss">rss</a> <a id="matrix" href="/blog.rss">rss</a>
<a href="/blog.json">json</a> <a id="matrix" href="/blog.json">json</a>
</span> </span>
</h1> </h1>
@ -20,14 +20,183 @@ Blog Articles
<ul> <ul>
{{ for article of search.pages("type=article", "order=asc date=desc")}} {{ for article of search.pages("type=article", "order=asc date=desc")}}
<li class="flex"> <li class="flex">
<!-- Nope, m-0 doesn't work! --> <a id="matrix" class="text-right font-mono" style="margin-right:calc(var(--spacing)*2)" href="{{ article.url }}">
<code style="margin:0" class="pr-1.25">
<a class="text-right" href="{{ article.url }}">
{{ article.date.toISOString().slice(2, 10).replaceAll("-", " ") }} {{ article.date.toISOString().slice(2, 10).replaceAll("-", " ") }}
</a> </a>
</code>
{{ article.title |> md }} {{ article.title |> md }}
</li> </li>
{{ /for }} {{ /for }}
</ul> </ul>
<script>
{
let typed = "";
const target = "mat";
document.addEventListener("keydown", (event) => {
typed += event.key.toLowerCase();
if (typed.length > target.length) typed = typed.slice(-target.length);
if (typed === target) {
toggle();
typed = "";
}
});
let data = null;
const toggle = () => {
if (data) {
Object.values(data).forEach(({ interval, element, original }) => {
clearInterval(interval);
element.innerHTML = original;
element.style.color = "";
element.style.textShadow = "";
element.style.filter = "";
});
data = null;
return;
}
data = {};
document.querySelectorAll("#matrix").forEach((element, index) => {
const original = element.textContent;
const randomize = () => {
const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const color = dark ? "#00ff00" : "#00cc00";
const glowColor = dark ? "#00ff00" : "#00ff00";
element.style.color = color;
element.style.filter = `drop-shadow(0 0 5px ${glowColor})`;
element.innerHTML = original.replace(/\d/g, () => `<span${Math.random() > 0.5 ? "" : ` style="text-shadow: 0 0 2px ${glowColor}"`}>${Math.floor(Math.random() * 10)}</span>`);
};
randomize();
const interval = setInterval(randomize, 100);
data[index] = { interval, element, original };
});
};
}
</script>
<script>
{
let typed = "";
const target = "rix";
document.addEventListener("keydown", (event) => {
typed += event.key.toLowerCase();
if (typed.length > target.length) typed = typed.slice(-target.length);
if (typed === target) {
toggle();
typed = "";
}
});
const randomStrip = () => ({
xFactor: Math.random() * 0.90 + (1 - 0.90) / 2,
y: Math.random() * -window.innerHeight - 100,
deltaY: Math.random() * 1.7 + 1,
size: Math.floor(Math.random() * 16) + 8,
});
const strips = Array.from({ length: 60 }, () => randomStrip());
let data = null;
const toggle = () => {
if (data) {
cancelAnimationFrame(data.animationFrameId);
data.canvas.remove();
window.removeEventListener("resize", data.handleResize);
data = null;
return;
}
data = {};
data.canvas = document.body.appendChild(document.createElement("canvas"));
const { canvas } = data;
canvas.style.position = "fixed";
canvas.style.top = "0";
canvas.style.left = "0";
canvas.style.width = "100%";
canvas.style.height = "100%";
canvas.style.zIndex = "999999999";
canvas.style.pointerEvents = "none";
data.context = canvas.getContext("2d");
const { context } = data;
context.globalCompositeOperation = "lighter";
const handleResize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
handleResize();
window.addEventListener("resize", handleResize);
data.handleResize = handleResize;
const animate = () => {
const { canvas, context } = data;
context.clearRect(0, 0, canvas.width, canvas.height);
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 8;
context.shadowColor = "#94f475";
context.textBaseline = "top";
context.textAlign = "center";
for (const strip of strips) {
context.font = `${strip.size}px sans`;
if (strip.y > canvas.height + (strip.size * 40)) {
Object.assign(strip, randomStrip())
}
let { y: yCopy } = strip;
for (let i = 0; i < 20; i++) {
switch (true) {
case i < 1: context.fillStyle = "#cefbe4"; break;
case i < 2: context.fillStyle = "#81ec72"; break;
case i < 4: context.fillStyle = "#5cd646"; break;
case i < 8: context.fillStyle = "#54d13c"; break;
case i < 14: context.fillStyle = "#4ccc32"; break;
case i < 18: context.fillStyle = "#43c728"; break;
}
const characters = ["诶", "比", "西", "迪", "伊", "吉", "艾", "杰", "开", "哦", "屁", "提", "维"];
const character = characters[Math.floor(Math.random() * characters.length)];
context.fillText(character, strip.xFactor * canvas.width, yCopy);
// Deterministic but still random.
yCopy -= strips[i].size;
}
strip.y += strip.deltaY;
}
data.animationFrameId = requestAnimationFrame(animate);
};
animate();
};
}
</script>

View file

@ -35,5 +35,5 @@ Here are some other useful links as well:
const element = document.getElementById("bot-block"); const element = document.getElementById("bot-block");
element.href = real; element.href = real;
element.children[0].innerHTML = real.substring(7); element.children[0].textContent = real.substring(7);
</script> </script>

View file

@ -3,68 +3,18 @@ prevent_zoom: true
--- ---
<style> <style>
cube-face::after { html {
z-index: -1; font-size: min(9svw, 9svh, 4.5rem);
content: ""; overscroll-behavior: none;
height: inherit;
width: inherit;
position: absolute;
} }
.cube-face-front { cube-scene {
background: linear-gradient(to bottom, cyan, blue); height: 100%;
&::after {
background: linear-gradient(to bottom, white, magenta);
mask-image: linear-gradient(to left, magenta, transparent);
}
} }
.cube-face-top { cube-face {
background: linear-gradient(to bottom, lime, cyan); align-items: center;
justify-content: center;
&::after {
background: linear-gradient(to bottom, yellow, white);
mask-image: linear-gradient(to left, white, transparent);
}
}
.cube-face-back {
background: linear-gradient(to bottom, yellow, red);
&::after {
background: linear-gradient(to bottom, lime, black);
mask-image: linear-gradient(to left, black, transparent);
}
}
.cube-face-bottom {
background: linear-gradient(to bottom, blue, black);
&::after {
background: linear-gradient(to bottom, magenta, red);
mask-image: linear-gradient(to left, red, transparent);
}
}
.cube-face-right {
background: linear-gradient(to bottom, white, magenta);
&::after {
background: linear-gradient(to bottom, yellow, red);
mask-image: linear-gradient(to left, red, transparent);
}
}
.cube-face-left {
background: linear-gradient(to bottom, lime, black);
&::after {
background: linear-gradient(to bottom, cyan, blue);
mask-image: linear-gradient(to left, blue, transparent);
}
} }
</style> </style>
@ -91,4 +41,6 @@ prevent_zoom: true
<a draggable="false" class="{{ style }}" href="/blog/">blog</a> <a draggable="false" class="{{ style }}" href="/blog/">blog</a>
{{ /set }} {{ /set }}
{{ include "cube.vto" }} {{ set cube_size = "5rem" }}
{{ set cube_last = true }}
{{ include "rgbcube.vto" }}