Merge pull request #367 from rajnandan1/release/3.2.12

feat: add kenerTheme support and enhance footer HTML structure
This commit is contained in:
Raj Nandan Sharma
2025-03-31 17:17:30 +05:30
committed by GitHub
26 changed files with 1264 additions and 223 deletions

6
package-lock.json generated
View File

@@ -1,15 +1,17 @@
{
"name": "kener",
"version": "3.2.10",
"version": "3.2.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "kener",
"version": "3.2.10",
"version": "3.2.11",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.10",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.3",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-markdown": "^6.3.2",

View File

@@ -68,6 +68,8 @@
"type": "module",
"dependencies": {
"@babel/runtime": "^7.26.10",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.3",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-markdown": "^6.3.2",

View File

@@ -1,108 +1,10 @@
/*one is the class for dotted background*/
.dots-pattern {
position: absolute;
top: 0px;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background: linear-gradient(177deg, rgba(255, 137, 131, 0.5) 0%, rgba(35, 136, 224, 0.05) 60%);
clip-path: polygon(0 0, 100% 0, 100% 54%, 0% 100%);
}
.dots-pattern::after {
content: "";
position: absolute;
background-image: radial-gradient(rgba(0, 0, 0, 0) 1.5px, var(--background-kener) 1px);
background-size: 14px 14px;
width: 100%;
height: 100vh;
top: 0;
transform: blur(3px);
left: 0;
}
.squares-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
/* clip-path: polygon(0 0, 100% 0, 100% 54%, 0% 100%); */
}
.squares-pattern::after {
content: "";
position: absolute;
background-image:
linear-gradient(#444cf7 1px, transparent 1px),
linear-gradient(to right, #444cf7 1px, var(--background-kener) 1px);
-webkit-mask-image: linear-gradient(
180deg,
rgba(0, 0, 0, 1) 0%,
rgba(0, 0, 0, 0.8) 20%,
rgba(0, 0, 0, 0.6) 40%,
rgba(0, 0, 0, 0.4) 60%,
rgba(0, 0, 0, 0.2) 80%,
rgba(0, 0, 0, 0) 100%
);
mask-image: linear-gradient(
180deg,
rgba(0, 0, 0, 0.2) 0%,
rgba(0, 0, 0, 0.1) 20%,
rgba(0, 0, 0, 0.05) 40%,
rgba(0, 0, 0, 0.025) 60%,
rgba(0, 0, 0, 0.0125) 80%,
rgba(0, 0, 0, 0) 100%
);
background-size: 32px 32px;
width: 100%;
height: 100vh;
top: 0;
left: 0;
}
.dark .squares-pattern::after {
-webkit-mask-image: linear-gradient(
to bottom,
rgba(0, 0, 0, 0.5) 0%,
rgba(0, 0, 0, 0.25) 20%,
rgba(0, 0, 0, 0.125) 40%,
rgba(0, 0, 0, 0.0625) 60%,
rgba(0, 0, 0, 0.03125) 80%,
rgba(0, 0, 0, 0) 100%
);
mask-image: linear-gradient(
180deg,
rgba(0, 0, 0, 0.5) 0%,
rgba(0, 0, 0, 0.25) 20%,
rgba(0, 0, 0, 0.125) 40%,
rgba(0, 0, 0, 0.0625) 60%,
rgba(0, 0, 0, 0.03125) 80%,
rgba(0, 0, 0, 0) 100%
);
background-image:
linear-gradient(#616fbf 1px, transparent 1px),
linear-gradient(to right, #616fbf 1px, var(--background-kener) 1px);
}
/*Needed to overlay content on top of dotted bg*/
section {
position: relative;
z-index: 1;
}
/*Needed overlay content on top of dotted bg*/
.blurry-bg {
/* background-color: var(--background-kener-rgba);
box-shadow: 0 0 64px 64px var(--background-kener-rgba); */
}
:root {
--up-color: #4ead94;
@@ -952,3 +854,25 @@ textarea::placeholder {
p code:not([class^="language-"]) {
@apply rounded-sm bg-yellow-100 px-1 py-0.5 font-mono text-xs dark:bg-neutral-700 dark:text-neutral-100;
}
.kener-theme-mono .section-hero .h1,
.kener-theme-mono-hero-h1 {
@apply text-foreground;
}
.kener-theme-default .section-hero .h1,
.kener-theme-default-hero-h1 {
@apply bg-gradient-to-r from-green-300 via-blue-500 to-purple-600 bg-clip-text text-transparent;
}
.kener-theme-sunset .section-hero .h1,
.kener-theme-sunset-hero-h1 {
@apply bg-gradient-to-r from-yellow-500 via-red-500 to-pink-500 bg-clip-text text-transparent;
}
.kener-theme-forest .section-hero .h1,
.kener-theme-forest-hero-h1 {
@apply bg-gradient-to-l from-blue-500 via-teal-500 to-green-500 bg-clip-text text-transparent;
}
.kener-theme-ocean .section-hero .h1,
.kener-theme-ocean-hero-h1 {
@apply bg-gradient-to-l from-cyan-500 via-blue-500 to-indigo-500 bg-clip-text text-transparent;
}

View File

@@ -66,7 +66,7 @@ export const WebhookJSONTemplate = JSON.stringify(
actions: [
{
text: "{{action_text}}",
url: "{{action_url}}",
url: "{{{action_url}}}",
},
],
},

View File

@@ -8,6 +8,7 @@
import * as Select from "$lib/components/ui/select";
import locales from "$lib/locales/locales.json?raw";
import GMI from "$lib/components/gmi.svelte";
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
import Loader from "lucide-svelte/icons/loader";
import X from "lucide-svelte/icons/x";
@@ -16,6 +17,11 @@
import Play from "lucide-svelte/icons/play";
import { Tooltip } from "bits-ui";
import CodeMirror from "svelte-codemirror-editor";
import { html } from "@codemirror/lang-html";
import { githubLight, githubDark } from "@uiw/codemirror-theme-github";
import { mode } from "mode-watcher";
export let data;
let formStateHero = "idle";
@@ -34,6 +40,15 @@
let homeIncidentCount = 10;
let homeIncidentStartTimeWithin = 30;
let incidentGroupView = "EXPAND_FIRST";
let kenerTheme = "default";
let availableThemes = [
{ name: "Default", value: "default" },
{ name: "Mono", value: "mono" },
{ name: "Sunset", value: "sunset" },
{ name: "Forest", value: "forest" },
{ name: "Ocean", value: "ocean" }
];
let nav = [];
let categories = [];
@@ -74,6 +89,9 @@
if (data.siteData.footerHTML) {
footerHTML = data.siteData.footerHTML;
}
if (data.siteData.kenerTheme) {
kenerTheme = data.siteData.kenerTheme;
}
if (data.siteData.tzToggle) {
tzToggle = data.siteData.tzToggle;
}
@@ -105,6 +123,19 @@
heroError = data.error;
return;
}
saveTheme();
}
async function saveTheme() {
let resp = await storeSiteData({
kenerTheme: kenerTheme
});
//print data
let data = await resp.json();
if (data.error) {
alert(data.error);
return;
}
}
let navErrorMessage = "";
@@ -258,44 +289,16 @@
<Card.Description>Configure the hero section of your site.</Card.Description>
</Card.Header>
<Card.Content>
<form class="mx-auto mt-4 space-y-4" on:submit|preventDefault={formSubmitHero}>
<form class="mx-auto mt-4 flex flex-col gap-y-4" on:submit|preventDefault={formSubmitHero}>
<div class="flex w-full flex-row justify-evenly gap-2">
<div class="w-full">
<Label for="hero_title">
Title
<Tooltip.Root openDelay={100}>
<Tooltip.Trigger class="">
<Info class="inline-block h-4 w-4 text-muted-foreground" />
</Tooltip.Trigger>
<Tooltip.Content class="max-w-md">
<div
class=" flex items-center justify-center rounded border bg-gray-800 px-2 py-1.5 text-xs font-medium text-gray-300 shadow-popover outline-none"
>
You can set the title of the hero section here. This will be displayed on the top of the page.
</div>
</Tooltip.Content>
</Tooltip.Root>
</Label>
<Label for="hero_title">Title</Label>
<Input bind:value={hero.title} class="mt-2" type="text" id="hero_title" placeholder="Status Page for Kener" />
</div>
</div>
<div class="flex w-full flex-row justify-evenly gap-2">
<div class="w-full">
<Label for="hero_subtitle">
Subtitle
<Tooltip.Root openDelay={100}>
<Tooltip.Trigger class="">
<Info class="inline-block h-4 w-4 text-muted-foreground" />
</Tooltip.Trigger>
<Tooltip.Content class="max-w-md">
<div
class=" flex items-center justify-center rounded border bg-gray-800 px-2 py-1.5 text-xs font-medium text-gray-300 shadow-popover outline-none"
>
You can set the subtitle of the hero section here. This will be displayed below the title.
</div>
</Tooltip.Content>
</Tooltip.Root>
</Label>
<Label for="hero_subtitle">Subtitle</Label>
<Input
bind:value={hero.subtitle}
class="mt-2"
@@ -305,12 +308,49 @@
/>
</div>
</div>
<div class="flex flex-col gap-y-2">
<div>
<Label>Color</Label>
</div>
<div class="flex flex-row gap-x-2 rounded-md border p-2">
<div class="w-3/4 pt-2">
<span class="kener-theme-{kenerTheme}-hero-h1 font-medium">{hero.title}</span>
</div>
<div class="flex w-1/4 justify-end">
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button variant="outline">
{availableThemes.find((el) => el.value === kenerTheme)?.name || "Mono"}
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{#each availableThemes as theme}
<DropdownMenu.Group>
<DropdownMenu.Item>
<Button
variant="ghost"
class="h-6 w-full justify-start text-xs"
on:click={() => {
kenerTheme = theme.value;
}}
>
{theme.name}
</Button>
</DropdownMenu.Item>
</DropdownMenu.Group>
{/each}
</DropdownMenu.Content>
</DropdownMenu.Root>
</div>
</div>
</div>
<div class="flex w-full justify-end gap-x-2">
{#if !!heroError}
<div class="py-2 text-sm font-medium text-destructive">{heroError}</div>
{/if}
<Button type="submit" disabled={formStateHero === "loading"}>
Save
Save Hero Section
{#if formStateHero === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}
@@ -370,7 +410,7 @@
</div>
<div class="pt-8">
<Button type="submit" disabled={formStateIncident === "loading"}>
Save
Save Incidents Settings
{#if formStateIncident === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}
@@ -495,7 +535,7 @@
<span class="text-sm font-medium text-destructive"> {navErrorMessage} </span>
</p>
<Button type="submit" disabled={formStateHero === "loading"}>
Save
Save Navigation
{#if formStateHero === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}
@@ -574,7 +614,7 @@
<div class="py-2 text-sm font-medium text-destructive">{i18nErrorMessage}</div>
{/if}
<Button type="submit" disabled={formStatei18n === "loading"}>
Save
Save Languages
{#if formStatei18n === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}
@@ -637,7 +677,7 @@
<div class="py-2 text-sm font-medium text-destructive">{categoriesErrorMessage}</div>
{/if}
<Button type="submit" disabled={formStateCategories === "loading"}>
Save
Save Categories
{#if formStateCategories === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}
@@ -656,24 +696,42 @@
<form class="mx-auto mt-4 space-y-4" on:submit|preventDefault={formSubmitFooter}>
<div class="flex w-full flex-row justify-evenly gap-2">
<div class="w-full">
<textarea
bind:value={footerHTML}
class="mt-2 h-48 w-full rounded-sm border p-2"
placeholder="eg. <p>Powered by Kener</p>"
></textarea>
<div class="overflow-hidden rounded-md">
<CodeMirror
bind:value={footerHTML}
lang={html()}
theme={$mode == "dark" ? githubDark : githubLight}
styles={{
"&": {
width: "100%",
maxWidth: "100%",
height: "320px",
border: "1px solid hsl(var(--border) / var(--tw-border-opacity))",
borderRadius: "0.375rem"
}
}}
/>
</div>
</div>
</div>
<div class="flex w-full justify-end gap-x-2">
{#if !!footerErrorMessage}
<div class="py-2 text-sm font-medium text-destructive">{footerErrorMessage}</div>
{/if}
<Button type="submit" disabled={formStateFooter === "loading"}>
Save
{#if formStateFooter === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
<div class="flex w-full justify-between gap-x-2">
<div>
<Button variant="outline" class="mt-2" on:click={() => (footerHTML = data.defaultFooterHTML)}>
<span class="ml-2">Reset to default</span>
</Button>
</div>
<div class="flex flex-row justify-end gap-x-2">
{#if !!footerErrorMessage}
<div class="py-2 text-sm font-medium text-destructive">{footerErrorMessage}</div>
{/if}
</Button>
<Button type="submit" disabled={formStateFooter === "loading"}>
Save Custom Footer
{#if formStateFooter === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}
</Button>
</div>
</div>
</form>
</Card.Content>

View File

@@ -14,6 +14,11 @@
import { Tooltip } from "bits-ui";
import ColorPicker from "svelte-awesome-color-picker";
import CodeMirror from "svelte-codemirror-editor";
import { css } from "@codemirror/lang-css";
import { githubLight, githubDark } from "@uiw/codemirror-theme-github";
import { mode } from "mode-watcher";
export let data;
let formState = "idle";
@@ -26,6 +31,7 @@
barStyle: "PARTIAL",
barRoundness: "ROUNDED",
summaryStyle: "CURRENT",
kenerTheme: "default",
colorsJ: {
UP: "#4ead94",
DOWN: "#ca3038",
@@ -95,16 +101,64 @@
</p>
<div class="flex">
<RadioGroup.Root bind:value={themeData.pattern} onValueChange={(e) => (themeData.pattern = e)}>
<div class="flex items-center space-x-2">
<RadioGroup.Root
class="flex flex-1 flex-wrap justify-start gap-4"
bind:value={themeData.pattern}
onValueChange={(e) => (themeData.pattern = e)}
>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="squares" id="rsq" />
<Label for="rsq">Squares</Label>
</div>
<div class="flex items-center space-x-2">
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="dots" id="rdot" />
<Label for="rdot">Dots</Label>
</div>
<div class="flex items-center space-x-2">
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="tiles" id="tiles" />
<Label for="tiles">Tiles</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="radial-blue" id="radial-blue" />
<Label for="radial-blue">Radial Blue</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="radial-mono" id="radial-mono" />
<Label for="radial-mono">Radial Mono</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="radial-midnight" id="radial-midnight" />
<Label for="radial-midnight">Radial Midnight</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="circle-mono" id="circle-mono" />
<Label for="circle-mono">Circle Mono</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="carbon-fibre" id="carbon-fibre" />
<Label for="carbon-fibre">Carbon Fibre</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="texture-sky" id="texture-sky" />
<Label for="texture-sky">Texture Sky</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="angular-mono" id="angular-mono" />
<Label for="angular-mono">Angular Mono</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="angular-spring" id="angular-spring" />
<Label for="angular-spring">Angular Spring</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="angular-bloom" id="angular-bloom" />
<Label for="angular-bloom">Angular Bloom</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="pets" id="pets" />
<Label for="pets">Pets</Label>
</div>
<div class="flex w-1/6 items-center space-x-2">
<RadioGroup.Item value="none" id="rnone" />
<Label for="rnone">None</Label>
</div>
@@ -239,53 +293,51 @@
</RadioGroup.Root>
</div>
<hr />
<p class="font-medium">
Colors
<Tooltip.Root openDelay={100}>
<Tooltip.Trigger class="">
<Info class="inline-block h-4 w-4 text-muted-foreground" />
</Tooltip.Trigger>
<Tooltip.Content class="max-w-md">
<div
class=" flex items-center justify-center rounded border bg-gray-800 px-2 py-1.5 text-xs font-medium text-gray-300 shadow-popover outline-none"
>
Choose the colors for the monitor. You can choose between UP, DEGRADED, and DOWN.
</div>
</Tooltip.Content>
</Tooltip.Root>
</p>
<div class="flex w-full flex-row justify-evenly gap-2">
<div class="relative w-full">
<div>
<p class="font-medium">Status Colors</p>
<p class=" text-xs font-semibold text-muted-foreground">
Choose the colors for the monitor. You can choose between UP, DEGRADED, and DOWN.
</p>
</div>
<div class="flex w-full flex-row justify-start gap-x-8">
<div class="relative">
<ColorPicker
bind:hex={themeData.colorsJ.UP}
position="responsive"
isAlpha={false}
isDark={$mode == "dark"}
--input-size="16px"
isTextInput={true}
label="UP"
--cp-input-color="#777"
--cp-button-hover-color="#767676"
on:input={(event) => {
themeData.colorsJ.UP = event.detail.hex;
}}
/>
</div>
<div class="relative w-full">
<div class="relative">
<ColorPicker
bind:hex={themeData.colorsJ.DEGRADED}
position="responsive"
isAlpha={false}
isDark={$mode == "dark"}
--input-size="16px"
isTextInput={true}
label="DEGRADED"
--cp-input-color="#777"
--cp-button-hover-color="#767676"
on:input={(event) => {
themeData.colorsJ.DEGRADED = event.detail.hex;
}}
/>
</div>
<div class="relative w-full">
<div class="relative">
<ColorPicker
bind:hex={themeData.colorsJ.DOWN}
position="responsive"
isAlpha={false}
isDark={$mode == "dark"}
--input-size="16px"
isTextInput={true}
label="DOWN"
--cp-input-color="#777"
--cp-button-hover-color="#767676"
on:input={(event) => {
themeData.colorsJ.DOWN = event.detail.hex;
}}
@@ -338,14 +390,28 @@
<div class="flex w-full flex-row justify-start gap-2">
<div class="w-full">
<Label for="customCSS" class="text-sm text-muted-foreground">
You can add custom CSS to style your site. Do not include style tags.
You can add custom CSS to style your site. Do not include style tags. Go to <a
href="https://kener.ing/docs/custom-js-css-guide#grid-layout"
class="text-card-foreground"
target="_blank">this link</a
> to learn more about it.
</Label>
<textarea
bind:value={themeData.customCSS}
id="customCSS"
class="mt-1 h-48 w-full rounded-sm border p-2"
placeholder=".className&#123;color: red;&#125;"
></textarea>
<div class="overflow-hidden rounded-md">
<CodeMirror
bind:value={themeData.customCSS}
lang={css()}
theme={$mode == "dark" ? githubDark : githubLight}
styles={{
"&": {
width: "100%",
maxWidth: "100%",
height: "320px",
border: "1px solid hsl(var(--border) / var(--tw-border-opacity))",
borderRadius: "0.375rem"
}
}}
/>
</div>
</div>
</div>
<hr />
@@ -354,7 +420,7 @@
<div class="py-2 text-sm font-medium text-destructive">{themeError}</div>
{/if}
<Button type="submit" disabled={formState === "loading"}>
Save
Save Theme Settings
{#if formState === "loading"}
<Loader class="ml-2 inline h-4 w-4 animate-spin" />
{/if}

View File

@@ -8,11 +8,62 @@
import GMI from "$lib/components/gmi.svelte";
export let data;
let defaultPattern = data.site?.pattern || "squares";
let allPets = [
{
url: base + "/chicken.gif",
bottom: "-5"
},
{
url: base + "/dog.gif",
bottom: "-17"
},
{
url: base + "/cockatiel.gif",
bottom: "-10"
},
{
url: base + "/crab.gif",
bottom: "-20"
},
{
url: base + "/fox.gif",
bottom: "-9"
},
{
url: base + "/horse.gif",
bottom: "-11"
},
{
url: base + "/panda.gif",
bottom: "0"
},
{
url: base + "/totoro.gif",
bottom: "-27"
},
{
url: base + "/rabbit.gif",
bottom: "0"
},
{
url: base + "/duck.gif",
bottom: "-5"
},
{
url: base + "/snake.gif",
bottom: "0"
}
];
let randomPet = allPets[Math.floor(Math.random() * allPets.length)];
</script>
<div class="{defaultPattern}-pattern"></div>
{#if defaultPattern == "pets" && !!randomPet}
<div class="pets-pattern" style="background-image: url({randomPet.url});bottom: {randomPet.bottom}px"></div>
{:else}
<div class="{defaultPattern}-pattern"></div>
{/if}
<header class="blurry-bg sticky top-0 z-50 mx-auto md:mt-2">
<header class="sticky top-0 z-50 mx-auto md:mt-2">
<div class="container flex h-14 max-w-[820px] items-center border bg-card px-3 md:rounded-md">
<a rel="external" href={data.site.home ? data.site.home : base} class="mr-6 flex items-center space-x-2">
{#if data.site.logo}

View File

@@ -77,6 +77,11 @@ const siteDataKeys = [
isValid: (value) => typeof value === "string",
data_type: "string",
},
{
key: "kenerTheme",
isValid: (value) => typeof value === "string",
data_type: "string",
},
{
key: "customCSS",
isValid: (value) => typeof value === "string",
@@ -89,8 +94,25 @@ const siteDataKeys = [
},
{
key: "pattern",
//string dots or sqaures or circ
isValid: (value) => typeof value === "string" && ["dots", "squares", "none"].includes(value),
//string dots or squares or circle
isValid: (value) =>
typeof value === "string" &&
[
"dots",
"squares",
"tiles",
"none",
"radial-blue",
"radial-mono",
"radial-midnight",
"circle-mono",
"carbon-fibre",
"texture-sky",
"angular-mono",
"angular-spring",
"angular-bloom",
"pets",
].includes(value),
data_type: "string",
},
{

View File

@@ -43,8 +43,20 @@ const seedSiteData = {
title: "Build stunning status pages in minutes",
subtitle: "Let your users know what's going on.",
},
footerHTML:
'<p class="text-center">\nMade using \n<a href="https://github.com/rajnandan1/kener" target="_blank" rel="noreferrer" class="font-medium underline underline-offset-4">\n Kener\n</a>\nan open source status page system built with Svelte and TailwindCSS.<br/>\nCreated with ❤️ by <a href="https://rajnandan.com" target="_blank" rel="noreferrer" class="font-medium underline underline-offset-4">Raj Nandan Sharma</a>.\n</p>\n',
footerHTML: `<div
class="container relative mt-4 max-w-[655px]"
>
<div class="flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0">
<p class="text-center text-sm leading-loose text-muted-foreground ">
Made using
<a href="https://github.com/rajnandan1/kener" target="_blank" class="font-medium underline underline-offset-4">
Kener
</a>
an open source status page system built with Svelte and TailwindCSS.<br/>
Created with ❤️ by <a href="https://rajnandan.com" target="_blank" class="font-medium underline underline-offset-4">Raj Nandan Sharma</a>.
</p>
</div>
</div>`,
i18n: {
defaultLocale: "en",
locales: [{ code: "en", name: "English", selected: true, disabled: false }],
@@ -88,6 +100,7 @@ const seedSiteData = {
categories: [{ name: "Home", description: "Monitors for Home Page" }],
homeIncidentCount: 5,
homeIncidentStartTimeWithin: 30,
kenerTheme: "default",
};
export default seedSiteData;

View File

@@ -1,6 +1,7 @@
<script>
import "../../app.postcss";
import "../../kener.css";
import "../../theme.css";
import Nav from "$lib/components/nav.svelte";
import { onMount } from "svelte";
import { Input } from "$lib/components/ui/input";
@@ -84,6 +85,8 @@
let customCSS = `<style>${data.site.customCSS}</style>`;
if (!!data.site.favicon && !data.site.favicon.startsWith("http")) data.site.favicon = `${base}${data.site.favicon}`;
let kenerTheme = data.site.kenerTheme || "default";
</script>
<svelte:head>
@@ -108,6 +111,7 @@
--down-color: {data.site.colors.DOWN};
--degraded-color: {data.site.colors.DEGRADED}
"
class="kener-theme-{kenerTheme}"
>
<Nav {data} />
<div class="main-content min-h-[70vh]">
@@ -115,16 +119,8 @@
</div>
{#if !!data.site.footerHTML}
<footer class="z-10 py-6 md:px-8 md:py-0">
<div
class="container relative flex max-w-[655px] flex-col items-center justify-center gap-4 pl-0 md:h-24 md:flex-row"
>
<div class="flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0">
<p class="text-center text-sm leading-loose text-muted-foreground md:text-left">
{@html data.site.footerHTML}
</p>
</div>
</div>
<footer class="kener-footer z-10 py-6 md:px-8 md:py-0">
{@html data.site.footerHTML}
</footer>
{/if}
<div class="fixed bottom-4 right-4 z-20 flex flex-col rounded-md bg-background">

View File

@@ -77,24 +77,20 @@
<div class="mt-12"></div>
{#if data.hero}
<section class="section-hero mx-auto mb-8 flex w-full max-w-[655px] flex-1 flex-col items-start justify-center">
<div class="mx-auto max-w-screen-xl px-4 lg:flex lg:items-center">
<div class="blurry-bg mx-auto max-w-3xl text-center">
{#if data.hero.image}
<GMI src={data.hero.image} classList="m-auto mb-2 h-14 w-14" alt="" srcset="" />
{/if}
{#if data.hero.title}
<h1
class="bg-gradient-to-r from-green-300 via-blue-500 to-purple-600 bg-clip-text text-5xl font-extrabold leading-tight text-transparent"
>
{@html data.hero.title}
</h1>
{/if}
{#if data.hero.subtitle}
<h2 class="mx-auto mt-4 max-w-xl sm:text-xl">
{@html data.hero.subtitle}
</h2>
{/if}
</div>
<div class="mx-auto max-w-3xl text-center">
{#if data.hero.image}
<GMI src={data.hero.image} classList="m-auto mb-2 h-14 w-14" alt="" srcset="" />
{/if}
{#if data.hero.title}
<h1 class="h1 text-5xl font-extrabold leading-tight">
{@html data.hero.title}
</h1>
{/if}
{#if data.hero.subtitle}
<h2 class="mx-auto mt-4 max-w-xl sm:text-xl">
{@html data.hero.subtitle}
</h2>
{/if}
</div>
</section>
{/if}
@@ -123,7 +119,7 @@
{/if}
{#if data.allRecentIncidents.length + data.allRecentMaintenances.length > 0}
<section
class="section-events mx-auto mb-8 flex w-full max-w-[655px] flex-1 flex-col items-start justify-center backdrop-blur-[2px]"
class="section-events mx-auto mb-8 flex w-full max-w-[655px] flex-1 flex-col items-start justify-center"
id=""
>
<div class="mb-2 grid w-full grid-cols-2 gap-4">

View File

@@ -0,0 +1,7 @@
import seedSiteData from "$lib/server/db/seedSiteData.js";
export async function load({ parent }) {
return {
defaultFooterHTML: seedSiteData.footerHTML,
};
}

902
src/theme.css Normal file
View File

@@ -0,0 +1,902 @@
.dots-pattern {
position: absolute;
top: 0px;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background: linear-gradient(177deg, rgba(255, 137, 131, 0.5) 0%, rgba(35, 136, 224, 0.05) 60%);
clip-path: polygon(0 0, 100% 0, 100% 54%, 0% 100%);
}
.dots-pattern::after {
content: "";
position: absolute;
background-image: radial-gradient(rgba(0, 0, 0, 0) 1.5px, var(--background-kener) 1px);
background-size: 14px 14px;
width: 100%;
height: 100vh;
top: 0;
transform: blur(3px);
left: 0;
}
.squares-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
/* clip-path: polygon(0 0, 100% 0, 100% 54%, 0% 100%); */
}
.squares-pattern::after {
content: "";
position: absolute;
background-image:
linear-gradient(#444cf7 1px, transparent 1px),
linear-gradient(to right, #444cf7 1px, var(--background-kener) 1px);
-webkit-mask-image: linear-gradient(
180deg,
rgba(0, 0, 0, 1) 0%,
rgba(0, 0, 0, 0.8) 20%,
rgba(0, 0, 0, 0.6) 40%,
rgba(0, 0, 0, 0.4) 60%,
rgba(0, 0, 0, 0.2) 80%,
rgba(0, 0, 0, 0) 100%
);
mask-image: linear-gradient(
180deg,
rgba(0, 0, 0, 0.2) 0%,
rgba(0, 0, 0, 0.1) 20%,
rgba(0, 0, 0, 0.05) 40%,
rgba(0, 0, 0, 0.025) 60%,
rgba(0, 0, 0, 0.0125) 80%,
rgba(0, 0, 0, 0) 100%
);
background-size: 32px 32px;
width: 100%;
height: 100vh;
top: 0;
left: 0;
}
.dark .squares-pattern::after {
-webkit-mask-image: linear-gradient(
to bottom,
rgba(0, 0, 0, 0.5) 0%,
rgba(0, 0, 0, 0.25) 20%,
rgba(0, 0, 0, 0.125) 40%,
rgba(0, 0, 0, 0.0625) 60%,
rgba(0, 0, 0, 0.03125) 80%,
rgba(0, 0, 0, 0) 100%
);
mask-image: linear-gradient(
180deg,
rgba(0, 0, 0, 0.5) 0%,
rgba(0, 0, 0, 0.25) 20%,
rgba(0, 0, 0, 0.125) 40%,
rgba(0, 0, 0, 0.0625) 60%,
rgba(0, 0, 0, 0.03125) 80%,
rgba(0, 0, 0, 0) 100%
);
background-image:
linear-gradient(#616fbf 1px, transparent 1px),
linear-gradient(to right, #616fbf 1px, var(--background-kener) 1px);
}
.radial-blue-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image: radial-gradient(
circle at top right,
rgb(49, 157, 235) 0%,
rgb(49, 157, 235) 13%,
rgb(76, 166, 234) 13%,
rgb(76, 166, 234) 23%,
rgb(103, 176, 232) 23%,
rgb(103, 176, 232) 33%,
rgb(130, 185, 231) 33%,
rgb(130, 185, 231) 46%,
rgb(156, 194, 230) 46%,
rgb(156, 194, 230) 48%,
rgb(183, 203, 229) 48%,
rgb(183, 203, 229) 63%,
rgb(210, 213, 227) 63%,
rgb(210, 213, 227) 83%,
rgb(237, 222, 226) 83%,
rgb(237, 222, 226) 100%
);
}
.dark .radial-blue-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image: radial-gradient(
circle at bottom right,
rgb(42, 23, 131) 0%,
rgb(42, 23, 131) 20%,
rgb(42, 40, 158) 20%,
rgb(42, 40, 158) 40%,
rgb(29, 41, 133) 40%,
rgb(21, 34, 134) 60%,
rgb(29, 52, 145) 60%,
rgb(24, 47, 141) 80%,
rgb(25, 54, 140) 80%,
rgb(26, 58, 154) 100%
);
}
.radial-mono-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image: radial-gradient(
circle at top left,
rgb(195, 195, 195) 0%,
rgb(195, 195, 195) 2%,
rgb(202, 202, 202) 2%,
rgb(202, 202, 202) 23%,
rgb(209, 209, 209) 23%,
rgb(209, 209, 209) 55%,
rgb(217, 217, 217) 55%,
rgb(217, 217, 217) 80%,
rgb(224, 224, 224) 80%,
rgb(224, 224, 224) 86%,
rgb(231, 231, 231) 86%,
rgb(231, 231, 231) 96%,
rgb(238, 238, 238) 96%,
rgb(238, 238, 238) 100%
);
}
.dark .radial-mono-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image: radial-gradient(
circle at top left,
rgb(48, 48, 48) 0%,
rgb(48, 48, 48) 8%,
rgb(46, 46, 46) 8%,
rgb(46, 46, 46) 57%,
rgb(43, 43, 43) 57%,
rgb(43, 43, 43) 65%,
rgb(41, 41, 41) 65%,
rgb(41, 41, 41) 67%,
rgb(39, 39, 39) 67%,
rgb(39, 39, 39) 72%,
rgb(36, 36, 36) 72%,
rgb(36, 36, 36) 80%,
rgb(34, 34, 34) 80%,
rgb(34, 34, 34) 100%
);
}
.radial-midnight-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
radial-gradient(
circle at 11% 62%,
rgba(205, 205, 205, 0.04) 0%,
rgba(205, 205, 205, 0.04) 50%,
rgba(149, 149, 149, 0.04) 50%,
rgba(149, 149, 149, 0.04) 100%
),
radial-gradient(
circle at 78% 10%,
rgba(49, 49, 49, 0.04) 0%,
rgba(49, 49, 49, 0.04) 50%,
rgba(254, 254, 254, 0.04) 50%,
rgba(254, 254, 254, 0.04) 100%
),
radial-gradient(
circle at 92% 43%,
rgba(34, 34, 34, 0.04) 0%,
rgba(34, 34, 34, 0.04) 50%,
rgba(206, 206, 206, 0.04) 50%,
rgba(206, 206, 206, 0.04) 100%
),
linear-gradient(329deg, rgb(241, 77, 33), rgb(218, 227, 61));
}
.dark .radial-midnight-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
radial-gradient(
circle at 90% 24%,
rgba(209, 209, 209, 0.04) 0%,
rgba(209, 209, 209, 0.04) 50%,
rgba(160, 160, 160, 0.04) 50%,
rgba(160, 160, 160, 0.04) 100%
),
radial-gradient(
circle at 91% 63%,
rgba(45, 45, 45, 0.04) 0%,
rgba(45, 45, 45, 0.04) 50%,
rgba(87, 87, 87, 0.04) 50%,
rgba(87, 87, 87, 0.04) 100%
),
radial-gradient(
circle at 17% 2%,
rgba(124, 124, 124, 0.04) 0%,
rgba(124, 124, 124, 0.04) 50%,
rgba(117, 117, 117, 0.04) 50%,
rgba(117, 117, 117, 0.04) 100%
),
linear-gradient(88deg, rgb(33, 20, 105), rgb(1, 15, 13));
}
.circle-mono-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image: radial-gradient(
circle at center center,
rgb(252, 252, 252),
rgb(129, 129, 129)
);
}
.dark .circle-mono-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image: radial-gradient(circle at center center, rgb(71, 71, 71), rgb(8, 8, 8));
}
.carbon-fibre-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
radial-gradient(circle at center center, transparent, rgb(255, 255, 255)),
repeating-linear-gradient(
135deg,
rgb(255, 255, 255) 0px,
rgb(255, 255, 255) 12px,
transparent 12px,
transparent 19px,
rgb(255, 255, 255) 19px,
rgb(255, 255, 255) 24px,
transparent 24px,
transparent 28px
),
repeating-linear-gradient(
45deg,
rgb(245, 245, 245) 0px,
rgb(245, 245, 245) 7px,
transparent 7px,
transparent 14px
),
linear-gradient(90deg, rgb(255, 255, 255), rgb(255, 255, 255));
}
.dark .carbon-fibre-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
radial-gradient(circle at center center, rgba(33, 33, 33, 0), rgb(33, 33, 33)),
repeating-linear-gradient(
135deg,
rgb(33, 33, 33) 0px,
rgb(33, 33, 33) 1px,
transparent 1px,
transparent 4px
),
repeating-linear-gradient(
45deg,
rgb(56, 56, 56) 0px,
rgb(56, 56, 56) 5px,
transparent 5px,
transparent 6px
),
linear-gradient(90deg, rgb(33, 33, 33), rgb(33, 33, 33));
}
.texture-sky-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
repeating-linear-gradient(
45deg,
rgba(0, 0, 0, 0.04),
rgba(0, 0, 0, 0.08),
rgba(0, 0, 0, 0.05),
rgba(0, 0, 0, 0.04),
rgba(0, 0, 0, 0.01),
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.09),
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.03),
transparent,
rgba(0, 0, 0, 0.07),
rgba(0, 0, 0, 0.03),
rgba(0, 0, 0, 0.06) 4px
),
linear-gradient(90deg, rgb(193, 255, 253), rgb(59, 104, 247));
}
.dark .texture-sky-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
repeating-linear-gradient(
90deg,
transparent,
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.08),
rgba(0, 0, 0, 0.01),
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.05),
rgba(0, 0, 0, 0.08),
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.04),
rgba(0, 0, 0, 0.02),
rgba(0, 0, 0, 0.02),
rgba(0, 0, 0, 0.09),
rgba(0, 0, 0, 0.04),
rgba(0, 0, 0, 0.06),
rgba(0, 0, 0, 0.01) 3px
),
linear-gradient(0deg, rgb(28, 117, 231), rgb(72, 64, 87));
}
.tiles-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(45deg, rgb(255, 255, 255) 0%, transparent 36%),
repeating-linear-gradient(
135deg,
rgba(197, 197, 197, 0.1) 0px,
rgba(197, 197, 197, 0.1) 1px,
transparent 1px,
transparent 11px
),
repeating-linear-gradient(
45deg,
rgba(197, 197, 197, 0.1) 0px,
rgba(197, 197, 197, 0.1) 1px,
transparent 1px,
transparent 11px
),
linear-gradient(0deg, rgb(255, 255, 255), rgb(255, 255, 255));
}
.dark .tiles-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(90deg, rgb(33, 33, 33) 0%, transparent 59%),
repeating-linear-gradient(
45deg,
rgba(168, 168, 168, 0.1) 0px,
rgba(168, 168, 168, 0.1) 1px,
transparent 1px,
transparent 13px
),
repeating-linear-gradient(
135deg,
rgba(168, 168, 168, 0.1) 0px,
rgba(168, 168, 168, 0.1) 1px,
transparent 1px,
transparent 13px
),
linear-gradient(90deg, rgb(33, 33, 33), rgb(33, 33, 33));
}
.angular-mono-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(
67.5deg,
rgb(215, 215, 215) 0%,
rgb(215, 215, 215) 46%,
rgb(198, 198, 198) 46%,
rgb(198, 198, 198) 49%,
rgb(181, 181, 181) 49%,
rgb(181, 181, 181) 56%,
rgb(164, 164, 164) 56%,
rgb(164, 164, 164) 61%,
rgb(146, 146, 146) 61%,
rgb(146, 146, 146) 75%,
rgb(129, 129, 129) 75%,
rgb(129, 129, 129) 84%,
rgb(112, 112, 112) 84%,
rgb(112, 112, 112) 100%
),
linear-gradient(
22.5deg,
rgb(215, 215, 215) 0%,
rgb(215, 215, 215) 46%,
rgb(198, 198, 198) 46%,
rgb(198, 198, 198) 49%,
rgb(181, 181, 181) 49%,
rgb(181, 181, 181) 56%,
rgb(164, 164, 164) 56%,
rgb(164, 164, 164) 61%,
rgb(146, 146, 146) 61%,
rgb(146, 146, 146) 75%,
rgb(129, 129, 129) 75%,
rgb(129, 129, 129) 84%,
rgb(112, 112, 112) 84%,
rgb(112, 112, 112) 100%
),
linear-gradient(
112.5deg,
rgb(215, 215, 215) 0%,
rgb(215, 215, 215) 46%,
rgb(198, 198, 198) 46%,
rgb(198, 198, 198) 49%,
rgb(181, 181, 181) 49%,
rgb(181, 181, 181) 56%,
rgb(164, 164, 164) 56%,
rgb(164, 164, 164) 61%,
rgb(146, 146, 146) 61%,
rgb(146, 146, 146) 75%,
rgb(129, 129, 129) 75%,
rgb(129, 129, 129) 84%,
rgb(112, 112, 112) 84%,
rgb(112, 112, 112) 100%
),
linear-gradient(90deg, rgb(231, 231, 231), rgb(195, 195, 195));
background-blend-mode: overlay, overlay, overlay, normal;
}
.dark .angular-mono-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(
45deg,
rgb(46, 46, 46) 0%,
rgb(46, 46, 46) 22%,
rgb(81, 81, 81) 22%,
rgb(81, 81, 81) 75%,
rgb(115, 115, 115) 75%,
rgb(115, 115, 115) 77%,
rgb(150, 150, 150) 77%,
rgb(150, 150, 150) 82%,
rgb(184, 184, 184) 82%,
rgb(184, 184, 184) 93%,
rgb(219, 219, 219) 93%,
rgb(219, 219, 219) 98%,
rgb(253, 253, 253) 98%,
rgb(253, 253, 253) 100%
),
linear-gradient(
135deg,
rgb(46, 46, 46) 0%,
rgb(46, 46, 46) 22%,
rgb(81, 81, 81) 22%,
rgb(81, 81, 81) 75%,
rgb(115, 115, 115) 75%,
rgb(115, 115, 115) 77%,
rgb(150, 150, 150) 77%,
rgb(150, 150, 150) 82%,
rgb(184, 184, 184) 82%,
rgb(184, 184, 184) 93%,
rgb(219, 219, 219) 93%,
rgb(219, 219, 219) 98%,
rgb(253, 253, 253) 98%,
rgb(253, 253, 253) 100%
),
linear-gradient(
22.5deg,
rgb(46, 46, 46) 0%,
rgb(46, 46, 46) 22%,
rgb(81, 81, 81) 22%,
rgb(81, 81, 81) 75%,
rgb(115, 115, 115) 75%,
rgb(115, 115, 115) 77%,
rgb(150, 150, 150) 77%,
rgb(150, 150, 150) 82%,
rgb(184, 184, 184) 82%,
rgb(184, 184, 184) 93%,
rgb(219, 219, 219) 93%,
rgb(219, 219, 219) 98%,
rgb(253, 253, 253) 98%,
rgb(253, 253, 253) 100%
),
linear-gradient(
157.5deg,
rgb(46, 46, 46) 0%,
rgb(46, 46, 46) 22%,
rgb(81, 81, 81) 22%,
rgb(81, 81, 81) 75%,
rgb(115, 115, 115) 75%,
rgb(115, 115, 115) 77%,
rgb(150, 150, 150) 77%,
rgb(150, 150, 150) 82%,
rgb(184, 184, 184) 82%,
rgb(184, 184, 184) 93%,
rgb(219, 219, 219) 93%,
rgb(219, 219, 219) 98%,
rgb(253, 253, 253) 98%,
rgb(253, 253, 253) 100%
),
linear-gradient(90deg, rgb(58, 58, 58), rgb(76, 76, 76));
background-blend-mode: overlay, overlay, overlay, overlay, normal;
}
.angular-spring-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(90deg, rgb(188, 188, 188), rgb(172, 172, 172)),
linear-gradient(
45deg,
rgb(164, 46, 188) 0%,
rgb(164, 46, 188) 14%,
rgb(165, 82, 189) 14%,
rgb(165, 82, 189) 20%,
rgb(165, 117, 190) 20%,
rgb(165, 117, 190) 23%,
rgb(166, 153, 191) 23%,
rgb(166, 153, 191) 30%,
rgb(166, 188, 192) 30%,
rgb(166, 188, 192) 37%,
rgb(167, 224, 193) 37%,
rgb(167, 224, 193) 100%
),
linear-gradient(
135deg,
rgb(164, 46, 188) 0%,
rgb(164, 46, 188) 14%,
rgb(165, 82, 189) 14%,
rgb(165, 82, 189) 20%,
rgb(165, 117, 190) 20%,
rgb(165, 117, 190) 23%,
rgb(166, 153, 191) 23%,
rgb(166, 153, 191) 30%,
rgb(166, 188, 192) 30%,
rgb(166, 188, 192) 37%,
rgb(167, 224, 193) 37%,
rgb(167, 224, 193) 100%
);
background-blend-mode: overlay, overlay, normal;
}
.dark .angular-spring-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(0deg, rgb(59, 59, 59), rgb(197, 197, 197)),
linear-gradient(
45deg,
rgb(159, 116, 214) 0%,
rgb(159, 116, 214) 25%,
rgb(130, 95, 173) 25%,
rgb(130, 95, 173) 45%,
rgb(101, 74, 132) 45%,
rgb(101, 74, 132) 50%,
rgb(72, 54, 90) 50%,
rgb(72, 54, 90) 52%,
rgb(43, 33, 49) 52%,
rgb(43, 33, 49) 61%,
rgb(14, 12, 8) 61%,
rgb(14, 12, 8) 100%
),
linear-gradient(
135deg,
rgb(159, 116, 214) 0%,
rgb(159, 116, 214) 25%,
rgb(130, 95, 173) 25%,
rgb(130, 95, 173) 45%,
rgb(101, 74, 132) 45%,
rgb(101, 74, 132) 50%,
rgb(72, 54, 90) 50%,
rgb(72, 54, 90) 52%,
rgb(43, 33, 49) 52%,
rgb(43, 33, 49) 61%,
rgb(14, 12, 8) 61%,
rgb(14, 12, 8) 100%
);
background-blend-mode: overlay, overlay, normal;
}
.angular-bloom-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(
157.5deg,
rgb(95, 97, 179) 0%,
rgb(95, 97, 179) 16%,
rgb(115, 120, 184) 16%,
rgb(115, 120, 184) 20%,
rgb(135, 142, 188) 20%,
rgb(135, 142, 188) 32%,
rgb(155, 165, 193) 32%,
rgb(155, 165, 193) 38%,
rgb(175, 187, 198) 38%,
rgb(175, 187, 198) 48%,
rgb(195, 210, 203) 48%,
rgb(195, 210, 203) 51%,
rgb(215, 232, 207) 51%,
rgb(215, 232, 207) 59%,
rgb(235, 255, 212) 59%,
rgb(235, 255, 212) 100%
),
linear-gradient(
135deg,
rgb(95, 97, 179) 0%,
rgb(95, 97, 179) 16%,
rgb(115, 120, 184) 16%,
rgb(115, 120, 184) 20%,
rgb(135, 142, 188) 20%,
rgb(135, 142, 188) 32%,
rgb(155, 165, 193) 32%,
rgb(155, 165, 193) 38%,
rgb(175, 187, 198) 38%,
rgb(175, 187, 198) 48%,
rgb(195, 210, 203) 48%,
rgb(195, 210, 203) 51%,
rgb(215, 232, 207) 51%,
rgb(215, 232, 207) 59%,
rgb(235, 255, 212) 59%,
rgb(235, 255, 212) 100%
),
linear-gradient(
112.5deg,
rgb(95, 97, 179) 0%,
rgb(95, 97, 179) 16%,
rgb(115, 120, 184) 16%,
rgb(115, 120, 184) 20%,
rgb(135, 142, 188) 20%,
rgb(135, 142, 188) 32%,
rgb(155, 165, 193) 32%,
rgb(155, 165, 193) 38%,
rgb(175, 187, 198) 38%,
rgb(175, 187, 198) 48%,
rgb(195, 210, 203) 48%,
rgb(195, 210, 203) 51%,
rgb(215, 232, 207) 51%,
rgb(215, 232, 207) 59%,
rgb(235, 255, 212) 59%,
rgb(235, 255, 212) 100%
),
linear-gradient(90deg, rgb(236, 151, 145), rgb(201, 243, 43));
background-blend-mode: overlay, overlay, overlay, normal;
}
.dark .angular-bloom-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
background-repeat: no-repeat;
background-size: 100%;
height: 100svh;
background-image:
linear-gradient(
90deg,
rgb(168, 86, 201) 0%,
rgb(168, 86, 201) 3%,
rgb(140, 74, 173) 3%,
rgb(140, 74, 173) 11%,
rgb(112, 61, 145) 11%,
rgb(112, 61, 145) 20%,
rgb(85, 49, 118) 20%,
rgb(85, 49, 118) 49%,
rgb(57, 37, 90) 49%,
rgb(57, 37, 90) 71%,
rgb(29, 24, 62) 71%,
rgb(29, 24, 62) 72%,
rgb(1, 12, 34) 72%,
rgb(1, 12, 34) 100%
),
linear-gradient(
67.5deg,
rgb(168, 86, 201) 0%,
rgb(168, 86, 201) 3%,
rgb(140, 74, 173) 3%,
rgb(140, 74, 173) 11%,
rgb(112, 61, 145) 11%,
rgb(112, 61, 145) 20%,
rgb(85, 49, 118) 20%,
rgb(85, 49, 118) 49%,
rgb(57, 37, 90) 49%,
rgb(57, 37, 90) 71%,
rgb(29, 24, 62) 71%,
rgb(29, 24, 62) 72%,
rgb(1, 12, 34) 72%,
rgb(1, 12, 34) 100%
),
linear-gradient(
112.5deg,
rgb(168, 86, 201) 0%,
rgb(168, 86, 201) 3%,
rgb(140, 74, 173) 3%,
rgb(140, 74, 173) 11%,
rgb(112, 61, 145) 11%,
rgb(112, 61, 145) 20%,
rgb(85, 49, 118) 20%,
rgb(85, 49, 118) 49%,
rgb(57, 37, 90) 49%,
rgb(57, 37, 90) 71%,
rgb(29, 24, 62) 71%,
rgb(29, 24, 62) 72%,
rgb(1, 12, 34) 72%,
rgb(1, 12, 34) 100%
),
linear-gradient(
45deg,
rgb(168, 86, 201) 0%,
rgb(168, 86, 201) 3%,
rgb(140, 74, 173) 3%,
rgb(140, 74, 173) 11%,
rgb(112, 61, 145) 11%,
rgb(112, 61, 145) 20%,
rgb(85, 49, 118) 20%,
rgb(85, 49, 118) 49%,
rgb(57, 37, 90) 49%,
rgb(57, 37, 90) 71%,
rgb(29, 24, 62) 71%,
rgb(29, 24, 62) 72%,
rgb(1, 12, 34) 72%,
rgb(1, 12, 34) 100%
),
linear-gradient(90deg, rgb(95, 3, 74), rgb(35, 65, 96));
background-blend-mode: overlay, overlay, overlay, overlay, normal;
}
.tiles-pattern::after,
.radial-mono-pattern::after,
.angular-mono-pattern::after,
.angular-spring-pattern::after,
.angular-bloom-pattern::after,
.texture-sky-pattern::after,
.carbon-fibre-pattern::after,
.circle-mono-pattern::after,
.radial-midnight-pattern::after,
.radial-blue-pattern::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(
to bottom,
var(--background-kener-rgba) 0%,
var(--background-kener) 100%
);
}
.pets-pattern {
position: fixed;
left: -50px; /* Start offscreen */
width: 50px;
height: 50px;
z-index: 100;
background-repeat: no-repeat;
background-size: 100%;
animation: petWalking 45s linear infinite;
}
@keyframes petWalking {
0% {
left: -50px;
transform: scaleX(1); /* Dog facing right */
}
45% {
left: calc(100vw + 25px);
transform: scaleX(1); /* Still facing right */
}
50% {
left: calc(100vw + 50px);
transform: scaleX(-1); /* Flip to face left */
}
95% {
left: -25px;
transform: scaleX(-1); /* Still facing left */
}
100% {
left: -50px;
transform: scaleX(1); /* Flip back to face right */
}
}

BIN
static/chicken.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
static/cockatiel.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/crab.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
static/dog.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/duck.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
static/fox.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
static/horse.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
static/panda.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
static/rabbit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
static/snake.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
static/totoro.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
static/turtle.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -20,6 +20,8 @@ export default defineConfig(({ mode }) => ({
"@uiw/codemirror-theme-github",
"@codemirror/language-json",
"@codemirror/language-markdown",
"@codemirror/language-css",
"@codemirror/language-html",
],
},
plugins: [