15 Commits

Author SHA1 Message Date
4f4c85c85a Bump version to 1.3.3 2025-06-11 21:34:28 -07:00
40bf1f6169 Improve theme settings interface 2025-06-11 21:26:56 -07:00
1ebb927c66 Zakarya page: add link to Colormatic Git profile 2025-06-11 21:25:33 -07:00
52662a6e4e Zakarya page: break up bio 2025-06-11 21:24:29 -07:00
5d9bd0f369 Remove period from construction notice
It did not play well with the formatter
2025-06-11 21:22:46 -07:00
12a51f18d2 Image optimizations 2025-06-11 21:22:05 -07:00
ea0cc34ba8 Require use of Svelte Runes 2025-06-11 21:18:37 -07:00
7fe92c971b Format bg.svelte 2025-06-11 21:17:43 -07:00
6df7d0565b Merge branch 'main' of git.colormatic.org:ColormaticStudios/Colormatic-Website
Merge commit oopsies
2025-06-11 16:38:18 -07:00
7958230d34 BG: Only update entities if animated is true 2025-06-11 16:37:10 -07:00
35ef8877ab Fix output data structure
This naming is very bad and it confused me
oops
2025-05-23 11:16:40 -07:00
f81fa98851 Disable BG animation
I've made the tough decision to disable the BG animation by default.
It's just to heavy on performance and is somewhat distracting for some
people. I'm planning on making a toggle on it, but I'm not sure that
will be done by the next release.
2025-05-16 19:40:36 -07:00
d23558cbfd I accidentally committed the release archive
I made a lot of mistakes with this release, what's next?
2025-05-09 20:26:58 -07:00
4bffe03f04 Bump version 2025-05-09 10:48:51 -07:00
98ec534d2e Light theme nav modal bugfix 2025-05-09 10:47:37 -07:00
16 changed files with 185 additions and 84 deletions

View File

@ -1,6 +1,6 @@
{
"name": "colormatic-website",
"version": "1.2.1",
"version": "1.3.3",
"type": "module",
"scripts": {
"dev": "vite dev",

View File

@ -7,15 +7,22 @@
let { darkTheme = $bindable() } = $props();
let timeScale = 1;
let animated = false;
$effect(() => {
darkTheme;
for (let i = 0; i < gradients.length; i++) {
// This re-renders each gradient so their new color will be correct
let gradient = gradients[i];
gradient.color = getRandomColor();
gradient.prepareBuffer();
}
if (!animated && ctx) {
// Don't try to render if ctx hasn't initialized yet
render(false);
}
});
let particleImages: { [key: string]: HTMLImageElement } = {};
@ -58,8 +65,13 @@
return {};
});
Promise.all(Object.values(imagePromises)).then(() => {
render(false);
});
resize();
init();
render(false);
animate();
window.addEventListener("resize", resize);
@ -70,6 +82,10 @@
canvas.height = window.innerHeight;
canvasDpiScaler(canvas, ctx);
if (!animated) {
render(false);
}
}
const clamp = (val: number, min: number, max: number) =>
@ -190,7 +206,7 @@
ctx.save();
// The source images are black, so we are inverting them
// different amounts to get different shades of gray
ctx.filter = darkTheme ? "invert(0.15)" : "invert(0.8)";
ctx.filter = darkTheme ? "invert(0.1)" : "invert(0.9)";
// Draw center of rotation
// ctx.beginPath();
@ -286,14 +302,13 @@
}
function init() {
/*/
* Calculate the proper amount of particles
* 25920 is our constant, equal to x in (1080*1920)/x = 80
* Because the subjectively correct amount of particles for a 1080p
* display is 80, so to calculate the proper amount for any window size,
* just do (width * height) / 25920
/*/
let particleCount = (window.innerWidth * window.innerHeight) / 25920;
let isMobile = /Mobile|Android|iPhone/i.test(navigator.userAgent);
let particleCount: number;
if (isMobile) {
particleCount = 25;
} else {
particleCount = 40;
}
particles = [];
for (let i = 0; i < particleCount; i++) {
@ -305,20 +320,26 @@
}
}
function animate() {
function render(update: boolean) {
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
for (let i_gradient = 0; i_gradient < gradients.length; i_gradient++) {
let gradient = gradients[i_gradient];
gradient.update();
if (update) gradient.update();
gradient.draw();
}
for (let i_particle = 0; i_particle < particles.length; i_particle++) {
let particle = particles[i_particle];
particle.update();
if (update) particle.update();
particle.draw();
}
}
function animate() {
if (animated) {
render(true);
}
requestAnimationFrame(animate);
}

View File

@ -51,7 +51,7 @@ Svelte modal example, https://svelte.dev/playground/modal
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
<span onclick={modalMenuProcessClick} bind:this={pages} class="modalbg hidden">
<div class={darkTheme ? "dark-theme" : ""}>
<div class={darkTheme() ? "dark-theme" : ""}>
<button onclick={toggleModalMenu} class="close" aria-label="Close">
<i class="bi bi-x"></i>
</button>

View File

@ -3,6 +3,8 @@
import { themes } from "../script/theme.ts";
import { setCookie, getCookie } from "../script/cookie.ts";
let panelRef = $state() as HTMLElement;
let lightButtonIcon = $state() as HTMLElement;
let darkButtonIcon = $state() as HTMLElement;
let autoButtonIcon = $state() as HTMLElement;
@ -13,32 +15,28 @@
let { themeOption = $bindable() } = $props();
let expanded = $state(false);
let currentIcon = $state("bi-circle-half");
let darkTheme: CallableFunction = getContext("darkTheme");
function setThemeOption(newThemeOption: string) {
expanded = false;
setCookie("theme", newThemeOption);
themeOption = newThemeOption;
switch (newThemeOption) {
case themes.LIGHT:
themeOption = themes.LIGHT;
lightButtonIcon.classList.replace("bi-sun", "bi-sun-fill");
darkButtonIcon.classList.replace("bi-moon-fill", "bi-moon");
autoButtonIcon.classList.replace("bi-display-fill", "bi-display");
currentIcon = "bi-sun-fill";
break;
case themes.DARK:
themeOption = themes.DARK;
lightButtonIcon.classList.replace("bi-sun-fill", "bi-sun");
darkButtonIcon.classList.replace("bi-moon", "bi-moon-fill");
autoButtonIcon.classList.replace("bi-display-fill", "bi-display");
currentIcon = "bi-moon-fill";
break;
case themes.AUTO:
themeOption = themes.AUTO;
lightButtonIcon.classList.replace("bi-sun-fill", "bi-sun");
darkButtonIcon.classList.replace("bi-moon-fill", "bi-moon");
autoButtonIcon.classList.replace("bi-display", "bi-display-fill");
currentIcon = "bi-circle-half";
break;
default:
console.error("setThemeOption was passed a value that is not a theme");
console.error("Invalid theme option");
}
}
@ -47,59 +45,113 @@
if (Object.values(themes).includes(themeCookie)) {
setThemeOption(themeCookie);
}
function handleClickOutside(event: MouseEvent) {
if (panelRef && !panelRef.contains(event.target as HTMLElement)) {
expanded = false;
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
let darkTheme: CallableFunction = getContext("darkTheme");
function togglePanel() {
expanded = !expanded;
}
</script>
<div class="panel settings {darkTheme() ? 'dark-theme' : ''}">
<div
bind:this={panelRef}
class="theme-toggle panel {darkTheme() ? 'dark-theme' : ''}"
>
<button
aria-label="Dark Theme"
bind:this={darkButton}
onclick={() => {
setThemeOption(themes.DARK);
}}
class="toggle-button"
onclick={togglePanel}
aria-label="Toggle Theme Selector"
>
<i bind:this={darkButtonIcon} class="bi bi-moon"></i>
</button>
<button
aria-label="Light Theme"
bind:this={lightButton}
onclick={() => {
setThemeOption(themes.LIGHT);
}}
>
<i bind:this={lightButtonIcon} class="bi bi-sun"></i>
</button>
<button
aria-label="Auto Theme"
bind:this={autoButton}
onclick={() => {
setThemeOption(themes.AUTO);
}}
>
<i bind:this={autoButtonIcon} class="bi bi-display-fill"></i>
<i class={`bi ${currentIcon}`}></i>
</button>
<!-- Unfortunately, we have to hard-code 72px because CSS won't animate `auto` or `min-content` -->
<div class="button-group" style:height={expanded ? "68px" : "0px"}>
<!-- Don't show the button if it is currently selected (it will be shown as the dropdown icon) -->
{#if themeOption !== themes.DARK}
<button
aria-label="Dark Theme"
bind:this={darkButton}
onclick={() => setThemeOption(themes.DARK)}
>
<i bind:this={darkButtonIcon} class="bi bi-moon"></i>
</button>
{/if}
{#if themeOption !== themes.LIGHT}
<button
aria-label="Light Theme"
bind:this={lightButton}
onclick={() => setThemeOption(themes.LIGHT)}
>
<i bind:this={lightButtonIcon} class="bi bi-sun"></i>
</button>
{/if}
{#if themeOption !== themes.AUTO}
<button
aria-label="Auto Theme"
bind:this={autoButton}
onclick={() => setThemeOption(themes.AUTO)}
>
<i bind:this={autoButtonIcon} class="bi bi-circle-half"></i>
</button>
{/if}
</div>
</div>
<style lang="scss">
@use "../style/global.scss";
div.panel.settings {
padding: 6px 8px;
div.theme-toggle {
padding: 4px;
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
flex-direction: column-reverse;
align-items: flex-end;
}
div.panel.settings button {
color: global.$text-color;
font-size: 110%;
cursor: pointer;
// Reset buton style
button.toggle-button {
font-size: 1.3rem;
background: transparent;
border: none;
background-color: #00000000;
border-radius: 0;
cursor: pointer;
color: global.$text-color;
aspect-ratio: 1 / 1;
}
div.button-group {
overflow: hidden;
transition: height 0.3s ease;
display: flex;
flex-direction: column;
}
div.button-group button {
margin: 2px 0;
font-size: 1.3rem;
background: transparent;
border: none;
color: global.$text-color;
cursor: pointer;
}
@media screen and (max-width: global.$mobile-width) {
div.button-group button {
margin: 4px 0;
}
}
</style>

View File

@ -17,7 +17,7 @@
let darkTheme = $state(false);
/*/
* This is necesarry for pages to read the theme,
* This is necessary for pages to read the theme,
* sucks that we have to use an anonymous function
* just to grab a variable
/*/

View File

@ -1,2 +1,5 @@
export const prerender = true;
export const csr = true;
export const trailingSlash = "always";
// This will output directories containing index.html

View File

@ -101,7 +101,6 @@
>
ColormaticStudios/Colormatic-Website
</a>
.
</p>
</div>
</main>

View File

@ -30,29 +30,47 @@
</svelte:head>
<main class={darkTheme() ? "dark-theme" : ""}>
<img class="banner" src="/img/zakarya-banner.png" alt="Zakarya Banner" />
<img
class="banner"
src="/img/zakarya-banner.webp"
alt="Zakarya Banner"
srcset="/img/zakarya-banner.webp 960w, /img/zakarya-banner@2x.webp 1920w"
/>
<div class="hero panel profile">
<div class="nameplate">
<img
src="/img/zakarya-icon.png"
src="/img/zakarya-icon.webp"
class="zakarya-icon"
alt="Zakarya Icon"
srcset="/img/zakarya-icon.webp 540w, /img/zakarya-icon@2x.webp 1080w"
/>
<span class="name-title">Zakarya</span>
</div>
<p>
I am a software and game developer, I run Colormatic and Colormatic
Studios, and I primarily study computer science, psychology, and
linguistics.
<br />
I have an intrinsic urge to create, and that's what Colormatic is all about.
My works include world building, music, videos, 3D modeling, video games, websites,
programs, and more.
</p>
<div class="bio">
<p>
I am a software and game developer, I run Colormatic and Colormatic
Studios, and I primarily study computer science, psychology, and
linguistics.
</p>
<p>
I have an intrinsic urge to create, and that's what Colormatic is all
about. My works include world building, music, videos, 3D modeling,
video games, websites, programs, and more.
</p>
</div>
<div class="linktree-container">
<ul class="linktree">
<li>
<a
href="https://git.colormatic.org/zakarya"
target="_blank"
rel="noopener noreferrer"
>
Colormatic Git
</a>
</li>
<li>
<a
href="https://mstdn.party/@zakarya"
@ -156,7 +174,7 @@
font-size: 200%;
}
main div.profile p {
main div.profile div.bio {
font-size: 120%;
max-width: 50%;
padding: 16px;
@ -185,7 +203,7 @@
margin: 24px auto;
}
main div.profile p {
main div.profile div.bio {
max-width: unset;
}
@ -212,7 +230,7 @@
}
}
main.dark-theme div.profile p {
main.dark-theme div.profile div.bio {
border-color: #ffffff55;
}
</style>

View File

@ -1,7 +1,12 @@
// The magic cookie system is so stupid
export function setCookie(cname: string, cvalue: string) {
document.cookie = cname + "=" + cvalue + ";";
export function setCookie(
cname: string,
cvalue: string,
sameSite: string = "Lax",
) {
document.cookie =
cname + "=" + cvalue + ";" + "SameSite" + "=" + sameSite + ";";
}
// Credit: https://www.w3schools.com/js/js_cookies.asp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

View File

@ -7,6 +7,9 @@ const config = {
kit: {
adapter: adapter(),
},
compilerOptions: {
runes: true,
},
};
export default config;