Massive refactor: integrate Tailwind, use components, data-driven lists

This is the result of many, many hours sat at my keyboard trying
different things, most of which failing.

Figuring out how to install Tailwind in this setup was a real adventure
with all of the resources online pulling me in two different ways.
Documentation is hard.

I definitely do prefer having the codebase use the tools available more,
instead of the old simple web dev stuff I'm used to. We're using what's
available through Svelte a lot more now.

I think that these changes will make the codebase easier to work in,
with the architecture being more familiar to regular web developers, and
these changes should also reduce friction when adding new content, which
is the main goal.

I'll never get this time back though, and I'm still slightly
contemplating whether it was really worth it.
This commit is contained in:
2025-07-04 10:33:30 -07:00
parent a2058f08d4
commit be8716a6a0
24 changed files with 665 additions and 1048 deletions

View File

@ -13,7 +13,7 @@
darkTheme;
for (let i = 0; i < gradients.length; i++) {
// This re-renders each gradient so their new color will be correct
// This recolors each gradient so their new color will be correct
let gradient = gradients[i];
gradient.color = getRandomColor();
gradient.prepareBuffer();

17
src/blocks/footer.svelte Normal file
View File

@ -0,0 +1,17 @@
<span
class="mx-auto flex w-[95%] flex-col border-t
border-t-[var(--text-color)] p-1 text-center lg:flex-row lg:justify-between"
>
<p class="py-4 lg:px-[10%]">
© 2025 Colormatic Studios, All Rights Reserved.
</p>
<p class="py-4 lg:px-[10%]">
<a href="mailto:support@colormatic.org" class="underline">
support@colormatic.org
</a>
<span class="hidden lg:inline">|</span>
<a href="mailto:contact@colormatic.org" class="underline">
contact@colormatic.org
</a>
</p>
</span>

206
src/blocks/navbar.svelte Normal file
View File

@ -0,0 +1,206 @@
<script lang="ts">
import { getContext } from "svelte";
let modal = $state() as HTMLElement;
let modalOpenState = $state(false);
function toggleModalMenu() {
modalOpenState = !modalOpenState;
}
function modalHandleClickOutside(e: MouseEvent) {
if (e.target == modal) {
toggleModalMenu();
}
}
let darkTheme: CallableFunction = getContext("darkTheme");
</script>
<nav
class="mx-auto box-border grid w-[95%] grid-cols-[1fr_min-content_1fr]
items-center border-b border-b-[var(--text-color)] p-2 wrap-anywhere lg:p-3"
>
<a href="/" class="justify-self-start text-[140%] font-bold">Colormatic</a>
<img
src="/img/colormatic_logo.svg"
alt="Colormatic Logo"
class="h-[40px] max-w-[unset]"
/>
<div class="flex justify-self-end">
<ul
class="responsive-hidden hidden
flex-row lg:flex"
>
<!-- prettier-ignore -->
{#each [
{
label: "Zakarya",
link: "/zakarya"
},
{
label: "Colormatic Studios",
link: "/studios"
},
{
label: "About",
link: "/about"
}
] as item}
<li><a href={item.link}>{item.label}</a></li>
{/each}
</ul>
<a
href="https://git.colormatic.org"
class="git-icon"
target="_blank"
rel="noopener noreferrer"
aria-label="Colormatic Git"
>
<i class="bi bi-git text-[160%]"></i>
</a>
<button onclick={toggleModalMenu} class="menu-button" aria-label="menu">
<i class="bi bi-list"></i>
</button>
</div>
</nav>
<!--
The following Svelte ignore statements aren't ideal, but it seems to be
the only way to achieve a proper modal. They even do this in the
Svelte modal example, https://svelte.dev/playground/modal
-->
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
<span
onclick={modalHandleClickOutside}
bind:this={modal}
class="fixed top-0 left-0 z-1 m-auto h-full w-full bg-[#00000066]
{modalOpenState ? '' : 'hidden'}"
>
<div
class="modal-animate mx-auto mt-[100px] w-[30ch] rounded-lg p-4
shadow-[1px_1px_8px_#00000033] backdrop-blur-[5px] lg:w-[50ch]
{darkTheme() ? 'bg-[#000000bb]' : 'bg-[#ffffff88]'}"
>
<button
onclick={toggleModalMenu}
class="float-right w-min cursor-pointer text-[200%] text-[var(--text-color)]
duration-[0.2s] hover:text-[#21afff]"
aria-label="Close"
>
<i class="bi bi-x"></i>
</button>
<ul>
<!-- prettier-ignore -->
{#each [
{
label: "Home",
link: "/",
newTab: false
},
{
label: "Zakarya",
link: "/zakarya",
newTab: false
},
{
label: "Colormatic Studios",
link: "/studios",
newTab: false
},
{
label: "Colormatic Git",
link: "https://git.colormatic.org",
newTab: true
},
{
label: "Colormatic ID",
link: "https://auth.colormatic.org",
newTab: true
},
{
label: "About",
link: "/about",
newTab: false
}
] as item}
<li class="mx-[12px] my-[18px]">
{#if item.newTab}
<a
href="{item.link}"
target="_blank"
rel="noopener noreferrer"
class="text-color p-2 text-[120%]"
>
{item.label}
</a>
{:else}
<a
href="{item.link}"
class="text-color p-2 text-[120%]"
>
{item.label}
</a>
{/if}
</li>
{/each}
</ul>
</div>
</span>
<style lang="scss">
nav a,
nav button {
padding: 0 8px;
color: var(--text-color);
display: flex;
cursor: pointer;
align-items: center;
}
nav ul li {
display: grid;
align-items: center;
}
nav button.menu-button {
transition-duration: 0.2s;
}
nav button.menu-button:hover {
color: #21afff;
}
nav .menu-button i {
font-size: 230%;
/*/
* Ugly hack to account for the fact that the menu icon is off-center
* (Bootstrap please fix)
/*/
transform: translateY(-1px);
}
/*/
* Navigation modal section
/*/
div.modal-animate {
animation-name: modal-animate-in;
animation-duration: 0.4s;
}
@keyframes modal-animate-in {
from {
transform: translate(0px, -300px);
opacity: 0;
}
to {
transform: translate(0px, 0px);
opacity: 1;
}
}
</style>

View File

@ -2,6 +2,7 @@
import { getContext, onMount } from "svelte";
import { themes } from "../script/theme.ts";
import { setCookie, getCookie } from "../script/cookie.ts";
import Panel from "components/panel.svelte";
let panelRef = $state() as HTMLElement;
@ -64,95 +65,61 @@
}
</script>
<div
bind:this={panelRef}
class="theme-toggle panel {darkTheme() ? 'dark-theme' : ''}"
>
<button
class="toggle-button"
onclick={togglePanel}
aria-label="Toggle Theme Selector"
>
<i class={`bi ${currentIcon}`}></i>
</button>
<div bind:this={panelRef} class="fixed right-5 bottom-5 flex">
<Panel className="flex flex-col-reverse p-1">
<button
class="flex cursor-pointer items-center px-[5px] text-[1.3rem]"
onclick={togglePanel}
aria-label="Toggle Theme Selector"
>
<i class={`bi ${currentIcon}`}></i>
</button>
<!-- Unfortunately, we have to hard-code the pixel count 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}
<!-- Unfortunately, we have to hard-code the pixel count because CSS won't animate `auto` or `min-content` -->
<div
class="button-group flex flex-col overflow-hidden"
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)}
class="cursor-pointer text-[1.3rem]"
>
<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.LIGHT}
<button
aria-label="Light Theme"
bind:this={lightButton}
class="cursor-pointer text-[1.3rem]"
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>
{#if themeOption !== themes.AUTO}
<button
aria-label="Auto Theme"
bind:this={autoButton}
class="cursor-pointer text-[1.3rem]"
onclick={() => setThemeOption(themes.AUTO)}
>
<i bind:this={autoButtonIcon} class="bi bi-circle-half"></i>
</button>
{/if}
</div>
</Panel>
</div>
<style lang="scss">
@use "../style/global.scss";
div.theme-toggle {
padding: 4px;
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
flex-direction: column-reverse;
align-items: flex-end;
}
button.toggle-button {
font-size: 1.3rem;
background: transparent;
border: none;
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;
}
}
// Click target color animation

View File

@ -1,42 +0,0 @@
<footer>
<p>© 2025 Colormatic Studios, All Rights Reserved.</p>
<p>
<a href="mailto:support@colormatic.org">support@colormatic.org</a>
<span class="responsive-hidden">|</span>
<a href="mailto:contact@colormatic.org">contact@colormatic.org</a>
</p>
</footer>
<style lang="scss">
@use "../style/global.scss";
footer {
display: flex;
justify-content: space-between;
border-top: solid 1px global.$text-color;
width: 95%;
padding: 4px;
margin: 0 auto;
}
footer p {
color: global.$text-color;
padding: 4px 10%;
}
@media screen and (max-width: global.$mobile-width) {
footer {
flex-direction: column;
}
footer p {
text-align: center;
padding: 4px;
margin: 4px;
}
.responsive-hidden {
display: none;
}
}
</style>

View File

@ -1,287 +0,0 @@
<script lang="ts">
import { getContext } from "svelte";
let pages = $state() as HTMLElement;
function toggleModalMenu() {
pages.classList.toggle("hidden");
}
function modalMenuProcessClick(e: MouseEvent) {
if (e.target == pages) {
pages.classList.toggle("hidden");
}
}
let darkTheme: CallableFunction = getContext("darkTheme");
</script>
<nav>
<a href="/" class="title nav-left">Colormatic</a>
<img
src="/img/colormatic_logo.svg"
alt="Colormatic Logo"
class="nav-center colormatic-logo"
/>
<div class="nav-right inline">
<ul class="responsive-hidden">
<li><a href="/zakarya">Zakarya</a></li>
<li><a href="/studios">Colormatic Studios</a></li>
<li><a href="/about">About</a></li>
</ul>
<a
href="https://git.colormatic.org"
class="git-icon"
target="_blank"
rel="noopener noreferrer"
aria-label="Colormatic Git"
>
<i class="bi bi-git"></i>
</a>
<button onclick={toggleModalMenu} class="menu-button" aria-label="menu">
<i class="bi bi-list"></i>
</button>
</div>
</nav>
<!--
The following Svelte ignore statements aren't ideal, but it seems to be
the only way to achieve a proper modal. They even do this in the
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" : ""}>
<button onclick={toggleModalMenu} class="close" aria-label="Close">
<i class="bi bi-x"></i>
</button>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/zakarya">Zakarya</a></li>
<li><a href="/studios">Colormatic Studios</a></li>
<li>
<a
href="https://git.colormatic.org"
target="_blank"
rel="noopener noreferrer"
>
Colormatic Git
</a>
</li>
<li>
<a
href="https://auth.colormatic.org"
target="_blank"
rel="noopener noreferrer"
>
Colormatic ID
</a>
</li>
<li><a href="/about">About</a></li>
</ul>
</div>
</span>
<style lang="scss">
@use "../style/global.scss";
nav {
display: grid;
grid-template-columns: 1fr min-content 1fr;
align-items: center;
padding: 12px;
border-bottom: solid 1px global.$text-color;
z-index: 1;
margin: 0 auto;
width: 95%;
box-sizing: border-box;
overflow-wrap: anywhere;
}
nav .nav-left {
justify-self: left;
grid-column: 1;
}
nav .nav-center {
justify-self: center;
grid-column: 2;
}
nav .nav-right {
justify-self: right;
grid-column: 3;
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
}
nav ul li {
display: grid;
justify-items: center;
}
nav a,
nav button {
padding: 0 8px;
color: global.$text-color;
text-decoration: none;
display: flex;
cursor: pointer;
align-items: center;
}
nav a.title {
font-size: 140%;
font-weight: bold;
}
nav img.colormatic-logo {
width: auto;
height: 40px;
padding: 4px;
}
nav button.menu-button {
background: none;
border: none;
transition-duration: 0.2s;
}
nav button.menu-button:hover {
color: #21afff;
}
nav .menu-button i {
font-size: 230%;
/*/
* Ugly hack to account for the fact that the menu icon is off-center
* (Bootstrap please fix)
/*/
transform: translateY(-1px);
}
nav .git-icon i {
font-size: 140%;
}
nav div.inline {
display: flex;
}
@media screen and (max-width: global.$mobile-width) {
nav {
padding: 6px 0;
}
nav .git-icon i {
font-size: 150%;
}
nav ul.responsive-hidden {
display: none;
}
}
/*/
* Navigation modal section
/*/
span.modalbg.hidden {
display: none;
}
span.modalbg {
position: fixed;
z-index: 1;
padding: 0;
margin: auto;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
span.modalbg div {
width: 50ch;
margin: 100px auto;
padding: 16px;
color: global.$text-color;
border-radius: 8px;
box-shadow: 1px 1px 8px #00000033;
background-color: #ffffffbb;
backdrop-filter: blur(5px);
animation-name: modal-animate-in;
animation-duration: 0.4s;
}
@keyframes modal-animate-in {
from {
transform: translate(0px, -300px);
opacity: 0;
}
to {
transform: translate(0px, 0px);
opacity: 1;
}
}
span.modalbg div button.close {
float: right;
width: min-content;
background: none;
border: none;
outline: none;
cursor: pointer;
font-size: 200%;
color: global.$text-color;
transition-duration: 0.2s;
}
span.modalbg div button.close:hover {
color: #21afff;
}
span.modalbg div ul {
list-style-type: none;
margin: 0;
padding: 0;
}
span.modalbg div ul li {
margin: 18px 12px;
}
span.modalbg div ul li a {
color: global.$text-color;
padding: 8px;
text-decoration: none;
font-size: 120%;
}
span.modalbg div ul li a:hover {
color: #21afff;
}
@media screen and (max-width: global.$mobile-width) {
span.modalbg div {
width: 30ch;
}
}
span.modalbg div.dark-theme {
background-color: #000000bb;
}
</style>

View File

@ -0,0 +1 @@
<div class="m-[24px] border-t border-t-[var(--text-color)]"></div>

View File

@ -0,0 +1,16 @@
<script lang="ts">
import Panel from "components/panel.svelte";
interface Props {
className?: string;
children?: import("svelte").Snippet;
}
let { className, children }: Props = $props();
</script>
<Panel
className="mx-auto my-4 w-[90%] lg:w-[60%] p-6 text-center
{className}"
>
{@render children?.()}
</Panel>

View File

@ -0,0 +1,40 @@
<script lang="ts">
interface Props {
className?: string;
links: {
label: string;
url: string;
}[];
}
let { className, links }: Props = $props();
</script>
<ul
class="{className}"
>
{#each links as link}
<li class="my-3 text-center">
<a
class="box-border inline-block w-full rounded-full
bg-[#4c5053] px-4 py-2 no-underline
shadow-[2px_2px_4px_#00000066] transition duration-500 hover:bg-[#383c3f]"
href={link.url}
target="_blank"
rel="noopener noreferrer"
>
{link.label}
</a>
</li>
{/each}
</ul>
<style lang="scss">
/*/
* This may look out of place, but the background color of the links
* in the link tree don't change based on light/dark theme. The text
* color of the links should always be white.
/*/
ul li a {
color: white;
}
</style>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import { getContext } from "svelte";
let darkTheme: CallableFunction = getContext("darkTheme");
interface Props {
className?: string;
children?: import("svelte").Snippet;
}
let { className, children }: Props = $props();
</script>
<div
class="rounded-[8px] border bg-[#ffffff22]
shadow-[1px_1px_8px_#00000033] backdrop-blur-[5px]
{className}
{darkTheme() ? 'border-[#ffffff33]' : 'border-[#00000033]'}"
>
{@render children?.()}
</div>

View File

@ -0,0 +1,12 @@
<span class="spacer"></span>
<style lang="scss">
span.spacer {
display: block;
margin-top: 24%;
@media (width >= 64rem) {
margin-top: 8%;
}
}
</style>

View File

@ -1,11 +1,12 @@
<script lang="ts">
import "../style/main.scss";
import "style/main.scss";
import "style/tailwind.css";
import "bootstrap-icons/font/bootstrap-icons.css";
import Navbar from "../component/navbar.svelte";
import Footer from "../component/footer.svelte";
import Bg from "../component/bg.svelte";
import Settings from "../component/settings.svelte";
import { themes } from "../script/theme.ts";
import Navbar from "blocks/navbar.svelte";
import Footer from "blocks/footer.svelte";
import Bg from "blocks/bg.svelte";
import Settings from "blocks/settings.svelte";
import { themes } from "script/theme.ts";
import { onMount, setContext } from "svelte";
interface Props {

View File

@ -1,5 +1,8 @@
<script lang="ts">
import { getContext, onMount } from "svelte";
import Spacer from "components/spacer.svelte";
import Hero from "components/hero.svelte";
import Divider from "components/divider.svelte";
let atTop = $state(true);
@ -44,9 +47,12 @@
<meta property="og:type" content="website" />
</svelte:head>
<main class={darkTheme() ? "dark-theme" : ""}>
<div class="brand-heading">
<h1>Colormatic: A non-profit project for creation.</h1>
<main>
<div
class="px-[12px] py-[38%] text-center text-[300%] font-bold
lg:w-[70%] lg:p-[12%] lg:text-left lg:text-[350%]"
>
Colormatic: A non-profit project for creation.
</div>
<div class="scroll-arrow {atTop ? '' : 'hide'}">
@ -55,84 +61,80 @@
<div style="margin-top:calc(100vh - 500px);"></div>
<div class="heading">Featured Colormatic Studios Projects:</div>
<div class="hero panel">
<h1>
<a
href="https://git.colormatic.org/ColormaticStudios/quality-godot-first-person"
target="_blank"
rel="noopener noreferrer"
>
Quality First Person Controller
</a>
</h1>
<p>An actually good first person controller for the Godot Engine.</p>
<div class="divider"></div>
<h1>
<a
href="https://git.colormatic.org/ColormaticStudios/godot-bson"
target="_blank"
rel="noopener noreferrer"
>
BSON for Godot
</a>
</h1>
<p>A BSON serializer/deserializer for the Godot Engine</p>
<div class="p-3 text-center text-[250%]">
Featured Colormatic Studios Projects:
</div>
<Hero>
<!--
Prettier wants to pack all of this data into one line, probably a bug.
This must be formatted manually.
<spacer></spacer>
This issue happens in various places around the codebase, whenever we
stuff data directly into an #each statement. This is the most prominent
place though, so I'll stick this comment here.
<div class="hero panel">
<h1>
<i class="bi bi-tools" style="padding-right: 12px;"></i>
Notice that Prettier also won't format the code inside the #each statement,
which means that the Tailwind classes won't get sorted. I'll miss that
feature.
-->
<!-- prettier-ignore -->
{#each [
{
title: "Quality First Person Controller",
link: "https://git.colormatic.org/ColormaticStudios/quality-godot-first-person",
description: "An actually good first person controller for the Godot Engine."
},
{
title: "BSON for Godot",
link: "https://git.colormatic.org/ColormaticStudios/godot-bson",
description: "A BSON serializer/deserializer for the Godot Engine"
}
] as item, index}
{#if index !== 0}
<Divider />
{/if}
<a
href={item.link}
target="_blank"
rel="noopener noreferrer"
class="text-color text-[200%] font-bold underline"
>
{item.title}
</a>
<p class="pt-3">{item.description}</p>
{/each}
</Hero>
<Spacer />
<Hero>
<span class="text-[200%] font-bold">
<i class="bi bi-tools pr-3"></i>
This website is under construction.
</h1>
<p>
</span>
<p class="p-3">
Check up on progress and changes at <a
href="https://git.colormatic.org/ColormaticStudios/Colormatic-Website"
target="_blank"
rel="noopener noreferrer"
class="underline"
>
ColormaticStudios/Colormatic-Website
</a>
</p>
</div>
</Hero>
</main>
<spacer></spacer>
<Spacer />
<style lang="scss">
@use "../style/global.scss";
main div.brand-heading {
padding: 12%;
width: 60%;
}
main div.brand-heading h1 {
font-size: 300%;
}
main div.heading {
font-size: 250%;
text-align: center;
padding: 12px;
}
@media screen and (max-width: global.$mobile-width) {
main div.brand-heading {
padding: 38% 12px;
text-align: center;
width: initial;
}
main div.heading {
font-size: 200%;
}
}
/*/
* Yes, this isn't in Tailwind, but I really don't want to translate this
* animation because Tailwind animations are stupidly verbose. With that,
* I also didn't translate the CSS around it because I just don't want to.
/*/
main div.scroll-arrow {
text-align: center;
font-size: 200%;

View File

@ -1,4 +1,6 @@
<script lang="ts">
import Hero from "components/hero.svelte";
import Spacer from "components/spacer.svelte";
import { getContext } from "svelte";
let darkTheme: CallableFunction = getContext("darkTheme");
@ -26,27 +28,29 @@
<meta property="og:type" content="website" />
</svelte:head>
<spacer></spacer>
<Spacer />
<main class={darkTheme() ? "dark-theme" : ""}>
<div class="hero panel">
<h1>Colormatic: A non-profit project for creation.</h1>
<p class="justify">
<main>
<Hero>
<span class="text-[200%] font-bold">
Colormatic: A non-profit project for creation.
</span>
<p class="justify pt-3">
Colormatic is a non-profit project from Zakarya for creating a curated
collection of sub-projects that match a high-quality, high attention to
detail standard.
</p>
<p class="justify">
<p class="justify pt-3">
Colormatic is still in the early stages, so expect many things to change
in the near future.
</p>
<p class="justify">
<p class="justify pt-3">
Colormatic Studios is a creative studio dedicated to giving life to
Colormatic's projects. We are currently just a small group of passionate
volunteers working to build inspiring, intuitive and innovative creative
works.
</p>
</div>
</Hero>
</main>
<spacer></spacer>
<Spacer />

View File

@ -1,4 +1,8 @@
<script lang="ts">
import Panel from "components/panel.svelte";
import Hero from "components/hero.svelte";
import Spacer from "components/spacer.svelte";
import Linktree from "components/linktree.svelte";
import { getContext } from "svelte";
let darkTheme: CallableFunction = getContext("darkTheme");
@ -35,244 +39,142 @@
<meta property="og:type" content="website" />
</svelte:head>
<main class={darkTheme() ? "dark-theme" : ""}>
<div class="cs-title"><h1>Colormatic Studios</h1></div>
<div class="project-grid-container">
<div class="project-grid-box panel">
<h1>
<a
href="https://git.colormatic.org/ColormaticStudios/quality-godot-first-person"
target="_blank"
rel="noopener noreferrer"
>
Quality First Person Controller
</a>
</h1>
<div class="project-grid-box-contents">
<img
src="https://git.colormatic.org/ColormaticStudios/quality-godot-first-person/raw/branch/main/icon.svg"
alt="First Person Logo"
/>
<p>An actually good first person controller for the Godot Engine.</p>
</div>
</div>
<div class="project-grid-box panel">
<h1>
<a
href="https://git.colormatic.org/ColormaticStudios/godot-bson"
target="_blank"
rel="noopener noreferrer"
>
BSON for Godot
</a>
</h1>
<div class="project-grid-box-contents">
<img
src="https://git.colormatic.org/ColormaticStudios/godot-bson/raw/branch/main/icon.svg"
alt="Godot BSON Logo"
/>
<p>A BSON serializer/deserializer for the Godot Engine.</p>
</div>
</div>
<div class="project-grid-box panel">
<h1>A Silly Game</h1>
<div class="project-grid-box-contents">
<img src="/img/studios/hatcat.webp" alt="HatCat" />
<p>
This is a silly little game project to get us started.
<br />
Currently in closed pre-alpha.
</p>
</div>
</div>
<div class="project-grid-box panel">
<h1>ColorQuest</h1>
<div class="project-grid-box-contents">
<img
src="/img/studios/colorquest.png"
class="pixelart"
alt="ColorQuest"
/>
<p>
A simple browser MMORPG focused on social features.
<br />
Currently in closed pre-alpha.
</p>
</div>
</div>
<main>
<div
class="cs-title flex h-[200px] items-center justify-center bg-cover lg:h-[300px]"
>
<span
class="rounded-2xl bg-[#00000088] px-[38px] py-[28px] text-[200%] font-bold
text-white shadow-[1px_1px_8px_#00000033] backdrop-blur-[5px] lg:text-[300%]"
>
Colormatic Studios
</span>
</div>
<div class="hero panel">
<h1>About Us</h1>
<p class="justify">
<div class="mx-auto flex w-[90%] flex-col flex-wrap lg:w-[80%] lg:flex-row">
<!-- prettier-ignore -->
{#each [
{
title: "Quality First Person Controller",
link: "https://git.colormatic.org/ColormaticStudios/quality-godot-first-person",
description: "An actually good first person controller for the Godot Engine.",
image: "https://git.colormatic.org/ColormaticStudios/quality-godot-first-person/raw/branch/main/icon.svg",
pixelArt: false
},
{
title: "BSON for Godot",
link: "https://git.colormatic.org/ColormaticStudios/godot-bson",
description: "A BSON serializer/deserializer for the Godot Engine.",
image: "https://git.colormatic.org/ColormaticStudios/godot-bson/raw/branch/main/icon.svg",
pixelArt: false
},
{
title: "A Silly Game",
link: "",
description: "This is a silly little game project to get us started. Currently in closed pre-alpha.",
image: "/img/studios/hatcat.webp",
pixelArt: false
},
{
title: "ColorQuest",
link: "",
description: "A simple browser MMORPG focused on social features. Currently in closed pre-alpha.",
image: "/img/studios/colorquest.png",
pixelArt: true
}
] as item}
<Panel className="flex-1 my-2 lg:m-4 lg:min-w-[40%] lg:max-w-[50%] p-4">
<div class="p-4">
{#if item.link}
<a
href="{item.link}"
target="_blank"
rel="noopener noreferrer"
class="text-color underline text-center text-[200%] font-bold block"
>
{item.title}
</a>
{:else}
<span class="text-center text-[200%] font-bold block">{item.title}</span>
{/if}
</div>
<div class="flex">
<img
src="{item.image}"
alt="First Person Logo"
class="max-w-[120px] max-h-[150px] m-3 rounded-lg
{item.pixelArt ? 'pixelart' : ''}"
/>
<p class="pt-4">{item.description}</p>
</div>
</Panel>
{/each}
</div>
<Hero>
<span class="text-[200%] font-bold">About Us</span>
<p class="justify pt-3">
Colormatic Studios is a creative studio dedicated to giving life to
Colormatic's ambitious future.
</p>
<p class="justify">
<p class="justify pt-3">
We are currently just a passionate team of volunteers working to build
inspiring, intuitive and innovative creative works. We don't have many
projects right now, but we're working hard behind the scenes on ventures
we'll be introducing later.
inspiring, intuitive and innovative creative works. We don't have much to
show right now, but we're working hard behind the scenes on projects we'll
be introducing later.
</p>
</div>
</Hero>
<div class="hero panel">
<h1>Links:</h1>
<div class="double-linktree">
<ul class="linktree">
<li>
<a
href="https://mastodon.social/@colormaticstudios"
target="_blank"
rel="noopener noreferrer"
>
Mastodon
</a>
</li>
<li>
<a
href="https://www.instagram.com/colormaticstudios/"
target="_blank"
rel="noopener noreferrer"
>
Instagram
</a>
</li>
<li>
<a
href="https://www.youtube.com/@colormaticstudios"
target="_blank"
rel="noopener noreferrer"
>
Youtube
</a>
</li>
</ul>
<ul class="linktree">
<li>
<a
href="https://git.colormatic.org/ColormaticStudios"
target="_blank"
rel="noopener noreferrer"
>
Colormatic Git
</a>
</li>
<li>
<a
href="https://github.com/ColormaticStudios"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</a>
</li>
<li>
<a
href="https://bsky.app/profile/colormaticstudios.bsky.social"
target="_blank"
rel="noopener noreferrer"
>
Bluesky
</a>
</li>
</ul>
<Hero>
<span class="text-[200%] font-bold">Links:</span>
<div class="mx-auto flex lg:w-[60%]">
<Linktree
className="flex-1 mx-2"
links={[
{
label: "Mastodon",
url: "https://mastodon.social/@colormaticstudios",
},
{
label: "Instagram",
url: "https://www.instagram.com/colormaticstudios",
},
{
label: "Youtube",
url: "https://www.youtube.com/@colormaticstudios",
},
]}
/>
<Linktree
className="flex-1 mx-2"
links={[
{
label: "Colormatic Git",
url: "https://git.colormatic.org/ColormaticStudios",
},
{
label: "GitHub",
url: "https://github.com/ColormaticStudios",
},
{
label: "Bluesky",
url: "https://bsky.app/profile/colormaticstudios.bsky.social",
},
]}
/>
</div>
</div>
</Hero>
</main>
<spacer></spacer>
<Spacer />
<style lang="scss">
@use "../../style/global.scss";
div.cs-title {
background-image: url("/img/colormatic_banner.svg");
background-size: cover;
background-repeat: no-repeat;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
div.cs-title h1 {
color: white;
background-color: #00000088;
padding: 28px 38px;
border-radius: 16px;
font-size: 300%;
box-shadow: 1px 1px 8px #00000033;
backdrop-filter: blur(5px);
}
div.project-grid-container {
display: flex;
width: 80%;
margin-left: auto;
margin-right: auto;
flex-wrap: wrap;
justify-content: center;
}
@media screen and (max-width: global.$mobile-width) {
div.project-grid-container {
width: 90%;
}
div.cs-title {
height: 200px;
}
div.cs-title h1 {
font-size: 200%;
}
}
div.project-grid-container div.project-grid-box {
flex: 1;
margin: 16px;
padding: 16px;
min-width: 40%;
max-width: 50%;
}
div.project-grid-container div.project-grid-box h1 {
text-align: center;
}
div.project-grid-container div.project-grid-box h1 a {
color: global.$text-color;
}
// Bad code
div.project-grid-container div.project-grid-box h1 a:hover {
color: #21afff;
}
div.project-grid-container
div.project-grid-box
div.project-grid-box-contents {
/* Yes, this absurdly long element selector is a joke */
display: flex;
}
div.project-grid-container div.project-grid-box img {
max-width: 120px;
max-height: 150px;
margin: 12px;
border-radius: 8px;
}
@media screen and (max-width: global.$mobile-width) {
div.project-grid-container div.project-grid-box {
min-width: 90%;
max-width: 90%;
}
div.project-grid-container div.project-grid-box img {
max-width: 100px;
}
img.pixelart {
image-rendering: pixelated;
}
</style>

View File

@ -1,4 +1,7 @@
<script lang="ts">
import Hero from "components/hero.svelte";
import Linktree from "components/linktree.svelte";
import Spacer from "components/spacer.svelte";
import { getContext } from "svelte";
let darkTheme: CallableFunction = getContext("darkTheme");
@ -29,26 +32,30 @@
<meta property="og:type" content="website" />
</svelte:head>
<main class={darkTheme() ? "dark-theme" : ""}>
<main>
<img
class="banner"
class="mx-auto mt-8 mb-8 block w-[95%] rounded-2xl border border-[#00000033]
shadow-[1px_1px_8px_#00000033] lg:w-[70%]"
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">
<Hero className="flex flex-wrap items-center flex-col lg:flex-row">
<div class="flex grow lg:flex-col">
<img
src="/img/zakarya-icon.webp"
class="zakarya-icon"
class="mx-4 my-2 block h-[38px] rounded-xl lg:mx-auto lg:h-auto lg:w-[100px] lg:rounded-2xl"
alt="Zakarya Icon"
srcset="/img/zakarya-icon.webp 540w, /img/zakarya-icon@2x.webp 1080w"
/>
<span class="name-title">Zakarya</span>
<span class="text-[200%]">Zakarya</span>
</div>
<div class="bio">
<p>
<div
class="m-3 h-fit rounded-2xl border p-4 text-left text-[120%] lg:max-w-1/2
{darkTheme() ? 'border-[#ffffff33]' : 'border-[#00000033]'}"
>
<p class="mb-4">
I am a software and game developer, I run Colormatic and Colormatic
Studios, and I primarily study computer science, psychology, and
linguistics.
@ -60,213 +67,67 @@
</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"
target="_blank"
rel="noopener noreferrer"
>
Mastodon
</a>
</li>
<li>
<a
href="https://ko-fi.com/zakarya"
target="_blank"
rel="noopener noreferrer"
>
Ko-fi
</a>
</li>
<li>
<a
href="https://www.youtube.com/@czakarya"
target="_blank"
rel="noopener noreferrer"
>
Youtube
</a>
</li>
<li>
<a
href="https://github.com/CZakarya"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</a>
</li>
<!--<li><a href="https://www.reddit.com/user/CZakarya/" target="_blank" rel="noopener noreferrer">Reddit</a></li>-->
</ul>
<div class="w-[60%] grow lg:w-[unset]">
<Linktree
links={[
{
url: "https://git.colormatic.org/zakarya",
label: "Colormatic Git",
},
{
url: "https://mstdn.party/@zakarya",
label: "Mastodon",
},
{
url: "https://ko-fi.com/zakarya",
label: "Ko-fi",
},
{
url: "https://www.youtube.com/@czakarya",
label: "Youtube",
},
{
url: "https://github.com/CZakarya",
label: "GitHub",
},
]}
/>
</div>
</div>
</Hero>
<div class="hero panel">
<h1>Featured Videos:</h1>
<ul class="videolist">
<li>
<Hero>
<span class="text-[200%] font-bold">Featured Videos:</span>
<ul class="flex list-none flex-wrap justify-center pl-0">
<li class="p-2">
<a
href="https://www.youtube.com/watch?v=FWGCPIEM_-o"
target="_blank"
rel="noopener noreferrer"
>
<img
class="h-auto w-[250px] rounded-lg"
src="https://files.colormatic.org/zakarya/videos/thumbnail/wayforward.png"
alt="Video Thumbnail"
/>
<span class="title">The Way Forward</span>
<span class="block p-2 text-[120%]">The Way Forward</span>
</a>
</li>
<li>
<li class="p-2">
<a
href="https://www.youtube.com/watch?v=OPD8NqNu0nE"
target="_blank"
rel="noopener noreferrer"
>
<img
class="h-auto w-[250px] rounded-lg"
src="https://files.colormatic.org/zakarya/videos/thumbnail/helloworld.png"
alt="Video Thumbnail"
/>
<span class="title">Hello World</span>
<span class="block p-2 text-[120%]">Hello World</span>
</a>
</li>
</ul>
</div>
</Hero>
</main>
<spacer></spacer>
<style lang="scss">
@use "../../style/global.scss";
main img.banner {
display: block;
width: 70%;
margin: 32px auto 32px auto;
border: solid 1px #00000033;
border-radius: 16px;
box-shadow: 1px 1px 8px #00000033;
}
main div.profile {
display: flex;
flex-wrap: wrap;
align-items: center;
}
main div.profile div.nameplate {
flex-grow: 1;
display: flex;
flex-direction: column;
}
main div.profile div.nameplate img.zakarya-icon {
width: 100px;
border-radius: 16px;
margin: 8px auto;
display: block;
}
main div.profile div.nameplate span.name-title {
font-size: 200%;
}
main div.profile div.bio {
font-size: 120%;
max-width: 50%;
padding: 16px;
margin: 12px;
height: fit-content;
text-align: left;
/* text-align: justify;
text-justify: auto; */
border-radius: 16px;
border: solid 1px;
border-color: #383c3f33; // Same as text color but with alpha
}
main div.profile div.linktree-container {
flex-grow: 1;
}
main div.profile div.linktree-container ul.linktree {
width: 100%;
}
@media screen and (max-width: global.$mobile-width) {
main img.banner {
width: 95%;
border-radius: 12px;
margin: 24px auto;
}
main div.profile div.bio {
max-width: unset;
}
main div.profile div.linktree-container ul.linktree {
width: 60%;
}
main div.profile div.nameplate {
flex-direction: row;
align-items: end;
justify-content: center;
}
main div.profile div.nameplate img.zakarya-icon {
height: 1.25em;
width: auto;
font-size: 170%;
margin: 0px 12px;
border-radius: 10px;
}
main div.profile div.nameplate span.name-title {
font-size: 170%;
}
}
main.dark-theme div.profile div.bio {
border-color: #ffffff55;
}
ul.videolist {
list-style-type: none;
display: flex;
flex-wrap: wrap;
justify-content: center;
padding-left: 0;
}
ul.videolist li {
padding: 8px;
}
ul.videolist li a {
text-decoration: none;
}
ul.videolist li a img {
width: 250px;
height: auto;
border-radius: 8px;
}
ul.videolist li a span {
display: block;
color: global.$text-color;
font-size: 120%;
}
</style>
<Spacer />

View File

@ -1,2 +0,0 @@
$text-color: var(--text-color);
$mobile-width: 900px;

View File

@ -1,5 +1,3 @@
@use "global.scss";
:root {
--text-color: #383c3f;
}
@ -7,21 +5,17 @@
body {
font-family: "Noto Sans", sans-serif;
margin: 0;
color: global.$text-color;
color: var(--text-color);
}
@media (prefers-color-scheme: dark) {
body {
background-color: black; // Don't flashbang dark theme users
background-color: black; // Don't flashbang dark theme users (CSS loads faster than JS)
}
:root {
--text-color: white;
}
}
spacer {
display: block;
margin-top: 8%;
// Javascript does this
// :root {
// --text-color: white;
// }
}
a {
@ -44,144 +38,22 @@ a:visited:hover {
color: #21afff;
}
a.btn,
button.btn {
padding: 8px 18px;
border-radius: 6px;
font-size: 100px;
text-decoration: none;
margin: 8px;
a.text-color {
color: var(--text-color);
}
a.btn.lg,
button.btn.lg {
font-size: 120%;
}
a.btn.blue,
button.btn.blue {
background-color: #2194ff;
border: 1px solid #21afff;
color: white;
}
div.divider {
margin: 38px;
border-top: 1px solid global.$text-color;
}
div.panel {
color: global.$text-color;
border: solid 1px;
border-color: #00000033;
border-radius: 8px;
box-shadow: 1px 1px 8px #00000033;
background-color: #ffffff22;
backdrop-filter: blur(5px);
}
main.dark-theme div.panel {
border-color: #ffffff33;
}
div.panel.dark-theme {
border-color: #ffffff33;
}
main div.hero {
width: 60%;
margin: 16px auto;
padding: 16px;
text-align: center;
}
@media screen and (max-width: global.$mobile-width) {
spacer {
margin-top: 24%;
}
main div.hero {
width: 80%;
}
}
main div.hero h1 a {
color: global.$text-color;
a.text-color:hover {
color: #21afff;
}
p.justify {
text-align: justify;
text-justify: auto;
}
main div.hero p.justify {
width: 60%;
width: 100%;
margin-left: auto;
margin-right: auto;
}
main div.hero ul {
margin-left: auto;
margin-right: auto;
}
@media screen and (max-width: global.$mobile-width) {
main div.hero p.justify {
width: 100%;
}
}
ul.linktree {
list-style-type: none;
margin: 0;
padding: 8px;
padding-left: 8px;
width: 30%;
}
ul.linktree li {
margin: 12px;
text-align: center;
}
@media screen and (max-width: global.$mobile-width) {
ul.linktree {
@media (width >= 64rem) {
width: 60%;
}
ul.linktree li {
margin: 12px 0;
}
}
ul.linktree li a {
/* Pill button shape */
text-decoration: none;
color: white;
background-color: #4c5053;
padding: 8px;
border-radius: 50px;
box-shadow: 2px 2px 4px #00000066;
transition-duration: 0.5s;
display: inline-block;
width: 100%;
box-sizing: border-box;
}
ul.linktree li a:hover {
cursor: pointer;
background-color: #383c3f;
}
div.double-linktree {
display: flex;
justify-content: center;
}
div.double-linktree ul.linktree {
margin: 0;
}
img.pixelart {
image-rendering: pixelated;
}

7
src/style/tailwind.css Normal file
View File

@ -0,0 +1,7 @@
/*/
* I have no idea why Tailwind wants a file like this; why can't I just
* have an import statement in the +layout.svelte? Web devs seriously
* confuse me.
/*/
@import "tailwindcss";