mirror of https://github.com/VERT-sh/VERT.git
feat: dark mode (#7)
* feat: basic dark mode * feat: theme toggle * Completed dark mode and about page changes * Fix progressive blur on dark mode * feat: dynadark tailwind plugin for dark detection --------- Co-authored-by: Realmy <163438634+RealmyTheMan@users.noreply.github.com>
This commit is contained in:
parent
3568ecf29b
commit
cbef8dae0b
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"css.customData": [".vscode/tailwind.json"]
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"version": 1.1,
|
||||||
|
"atDirectives": [
|
||||||
|
{
|
||||||
|
"name": "@tailwind",
|
||||||
|
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@apply",
|
||||||
|
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@responsive",
|
||||||
|
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@screen",
|
||||||
|
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@variants",
|
||||||
|
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.2",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||||
|
"sass": "^1.80.7",
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
"tailwindcss": "^3.4.9",
|
"tailwindcss": "^3.4.9",
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-svelte": "^0.456.0",
|
"lucide-svelte": "^0.456.0",
|
||||||
"svelte-adapter-bun": "^0.5.2",
|
"svelte-adapter-bun": "^0.5.2",
|
||||||
|
"typescript-cookie": "^1.0.6",
|
||||||
"wasm-vips": "^0.0.11"
|
"wasm-vips": "^0.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover" class="%theme%">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,24 +1,16 @@
|
||||||
@import "tailwindcss/base";
|
@tailwind base;
|
||||||
@import "tailwindcss/components";
|
@tailwind components;
|
||||||
@import "tailwindcss/utilities";
|
@tailwind utilities;
|
||||||
|
|
||||||
@import url(@fontsource/lexend/400.css);
|
@import url(@fontsource/lexend/400.css);
|
||||||
@import url(@fontsource/lexend/500.css);
|
@import url(@fontsource/lexend/500.css);
|
||||||
@import url(@fontsource/azeret-mono/600.css);
|
@import url(@fontsource/azeret-mono/600.css);
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--accent-bg: hsl(303, 73%, 81%);
|
|
||||||
--accent-fg: hsl(0, 0, 10%);
|
|
||||||
--font-body: "Lexend", system-ui, -apple-system, BlinkMacSystemFont,
|
--font-body: "Lexend", system-ui, -apple-system, BlinkMacSystemFont,
|
||||||
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
||||||
"Helvetica Neue", sans-serif;
|
"Helvetica Neue", sans-serif;
|
||||||
--font-display: "Azeret Mono", var(--font-body);
|
--font-display: "Azeret Mono", var(--font-body);
|
||||||
--bg: hsl(0, 0%, 100%);
|
|
||||||
--fg: hsl(0, 0%, 10%);
|
|
||||||
--fg-muted: hsl(0, 0%, 50%);
|
|
||||||
--fg-muted-alt: hsl(0, 0%, 75%);
|
|
||||||
--fg-highlight: hsl(303, 52%, 42%);
|
|
||||||
--fg-failure: hsl(0, 67%, 49%);
|
|
||||||
--transition: linear(
|
--transition: linear(
|
||||||
0,
|
0,
|
||||||
0.006,
|
0.006,
|
||||||
|
@ -37,6 +29,50 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin light {
|
||||||
|
--accent-bg: hsl(303, 73%, 81%);
|
||||||
|
--accent-fg: hsl(0, 0, 10%);
|
||||||
|
--bg: hsl(0, 0%, 100%);
|
||||||
|
--bg-transparent: hsla(0, 0%, 100%, 0.6);
|
||||||
|
--fg: hsl(0, 0%, 10%);
|
||||||
|
--fg-muted: hsl(0, 0%, 50%);
|
||||||
|
--fg-muted-alt: hsl(0, 0%, 75%);
|
||||||
|
--fg-highlight: hsl(303, 61%, 47%);
|
||||||
|
--fg-failure: hsl(0, 67%, 49%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
--accent-bg: hsl(304, 41%, 21%);
|
||||||
|
--accent-fg: hsl(303, 73%, 81%);
|
||||||
|
--bg: hsl(0, 0%, 8%);
|
||||||
|
--bg-transparent: hsla(0, 0%, 8%, 0.8);
|
||||||
|
--fg: hsl(0, 0%, 90%);
|
||||||
|
--fg-muted: hsl(0, 0%, 50%);
|
||||||
|
--fg-muted-alt: hsl(0, 0%, 25%);
|
||||||
|
--fg-highlight: hsl(303, 64%, 65%);
|
||||||
|
--fg-failure: hsl(0, 67%, 80%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
@include dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body {
|
||||||
|
@include light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.light {
|
||||||
|
@include light;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
@include dark;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply text-foreground bg-background font-body overflow-x-hidden;
|
@apply text-foreground bg-background font-body overflow-x-hidden;
|
||||||
width: 100vw;
|
width: 100vw;
|
|
@ -0,0 +1,9 @@
|
||||||
|
import type { Handle } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
const theme = event.cookies.get("theme") ?? "";
|
||||||
|
const res = await resolve(event, {
|
||||||
|
transformPageChunk: ({ html }) => html.replace("%theme%", theme),
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
|
@ -38,7 +38,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#each links as { name, url } (url)}
|
{#each links as { name, url } (url)}
|
||||||
<a
|
<a
|
||||||
class="w-1/2 px-2 h-[calc(100%-16px)] mt-2 flex items-center justify-center rounded-xl relative font-display overflow-hidden"
|
class="w-1/2 px-2 h-[calc(100%-16px)] mt-2 flex items-center justify-center rounded-xl relative overflow-hidden font-medium"
|
||||||
href={url}
|
href={url}
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (shouldGoBack) {
|
if (shouldGoBack) {
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<div class="grid grid-cols-1 grid-rows-1">
|
<div class="grid grid-cols-1 grid-rows-1">
|
||||||
{#key name}
|
{#key name}
|
||||||
<span
|
<span
|
||||||
class="mix-blend-difference invert col-start-1 row-start-1 text-center"
|
class="mix-blend-difference invert dynadark:invert-0 col-start-1 row-start-1 text-center"
|
||||||
in:fly={{
|
in:fly={{
|
||||||
duration,
|
duration,
|
||||||
easing: quintOut,
|
easing: quintOut,
|
||||||
|
|
|
@ -17,4 +17,13 @@ class Files {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Theme {
|
||||||
|
public dark = $state(false);
|
||||||
|
public toggle = () => {
|
||||||
|
this.dark = !this.dark;
|
||||||
|
console.log(this.dark);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const files = new Files();
|
export const files = new Files();
|
||||||
|
export const theme = new Theme();
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import "../app.css";
|
import "../app.scss";
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import { blur, duration } from "$lib/animation";
|
import { blur, duration } from "$lib/animation";
|
||||||
import { quintOut } from "svelte/easing";
|
import { quintOut } from "svelte/easing";
|
||||||
import { files } from "$lib/store/index.svelte";
|
import { files, theme } from "$lib/store/index.svelte";
|
||||||
import Logo from "$lib/components/visual/svg/Logo.svelte";
|
import Logo from "$lib/components/visual/svg/Logo.svelte";
|
||||||
import featuredImage from "$lib/assets/VERT_Feature.webp";
|
import featuredImage from "$lib/assets/VERT_Feature.webp";
|
||||||
import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public";
|
import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public";
|
||||||
import FancyMenu from "$lib/components/functional/FancyMenu.svelte";
|
import FancyMenu from "$lib/components/functional/FancyMenu.svelte";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
import { SunIcon } from "lucide-svelte";
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { setCookie } from "typescript-cookie";
|
||||||
let { children, data } = $props();
|
let { children, data } = $props();
|
||||||
|
|
||||||
let shouldGoBack = writable(false);
|
let shouldGoBack = writable(false);
|
||||||
|
@ -46,6 +49,23 @@
|
||||||
goto("/");
|
goto("/");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!browser) return;
|
||||||
|
if (theme.dark) {
|
||||||
|
document.body.classList.add("dark");
|
||||||
|
document.body.classList.remove("light");
|
||||||
|
setCookie("theme", "dark", {
|
||||||
|
sameSite: "strict",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.body.classList.add("light");
|
||||||
|
document.body.classList.remove("dark");
|
||||||
|
setCookie("theme", "light", {
|
||||||
|
sameSite: "strict",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -68,7 +88,7 @@
|
||||||
<div class="flex justify-center mb-5 lg:hidden">
|
<div class="flex justify-center mb-5 lg:hidden">
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
class="px-6 relative h-16 mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:hidden flex"
|
class="px-4 relative h-14 mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:hidden flex"
|
||||||
>
|
>
|
||||||
<div class="h-6 w-24 items-center flex justify-center">
|
<div class="h-6 w-24 items-center flex justify-center">
|
||||||
<Logo />
|
<Logo />
|
||||||
|
@ -91,6 +111,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FancyMenu {links} {shouldGoBack} />
|
<FancyMenu {links} {shouldGoBack} />
|
||||||
|
<div class="h-16 px-4 flex items-center">
|
||||||
|
<button onclick={theme.toggle}>
|
||||||
|
<SunIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-screen-lg grid grid-cols-1 grid-rows-1 relative">
|
<div class="w-full max-w-screen-lg grid grid-cols-1 grid-rows-1 relative">
|
||||||
{#key data.pathname}
|
{#key data.pathname}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { theme } from "$lib/store/index.svelte";
|
||||||
|
import { getCookie, setCookie } from "typescript-cookie";
|
||||||
|
|
||||||
|
export const load = ({ data }) => {
|
||||||
|
if (!browser) return;
|
||||||
|
const themeStr = getCookie("theme");
|
||||||
|
if (typeof themeStr === "undefined") {
|
||||||
|
theme.dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
setCookie("theme", theme.dark ? "dark" : "light");
|
||||||
|
} else {
|
||||||
|
theme.dark = themeStr === "dark";
|
||||||
|
}
|
||||||
|
theme.dark = getCookie("theme") === "dark";
|
||||||
|
return data;
|
||||||
|
};
|
|
@ -83,10 +83,12 @@
|
||||||
|
|
||||||
<div class="[@media(max-height:768px)]:block mt-10 picker-fly">
|
<div class="[@media(max-height:768px)]:block mt-10 picker-fly">
|
||||||
<Uploader
|
<Uploader
|
||||||
isMobile={data.isMobile}
|
isMobile={data.isMobile || false}
|
||||||
bind:files={ourFiles}
|
bind:files={ourFiles}
|
||||||
onupload={runUpload}
|
onupload={runUpload}
|
||||||
acceptedFormats={[...new Set(converters.flatMap((c) => c.supportedFormats))]}
|
acceptedFormats={[
|
||||||
|
...new Set(converters.flatMap((c) => c.supportedFormats)),
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -63,18 +63,28 @@
|
||||||
class="font-display text-3xl mt-12 text-transition"
|
class="font-display text-3xl mt-12 text-transition"
|
||||||
style="--delay: {5 * multiplier}ms"
|
style="--delay: {5 * multiplier}ms"
|
||||||
>
|
>
|
||||||
👨💻 source code
|
🔗 resources
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mt-6 text-transition" style="--delay: {6 * multiplier}ms">
|
<ul class="list-disc list-inside mt-6">
|
||||||
VERT is licensed under AGPL-3.0, and the source code can be found on <a
|
<li class="text-transition" style="--delay: {6 * multiplier}ms">
|
||||||
class="hover:underline font-medium text-foreground-highlight"
|
<a
|
||||||
href="https://github.com/not-nullptr/VERT">GitHub</a
|
href="https://github.com/notnullptr/VERT"
|
||||||
>.
|
class="text-foreground-highlight hover:underline">Source code</a
|
||||||
</p>
|
> (hosted on GitHub, licensed under AGPL-3.0)
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="text-transition" style="--delay: {7 * multiplier}ms">
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/8XXZ7TFFrK"
|
||||||
|
class="text-foreground-highlight hover:underline"
|
||||||
|
>Discord server</a
|
||||||
|
> (for chit-chat, suggestions, and support)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2
|
<h2
|
||||||
class="font-display text-3xl mt-12 text-transition"
|
class="font-display text-3xl mt-12 text-transition"
|
||||||
style="--delay: {7 * multiplier}ms"
|
style="--delay: {8 * multiplier}ms"
|
||||||
>
|
>
|
||||||
🎨 credits
|
🎨 credits
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -83,7 +93,7 @@
|
||||||
<div class="hover:scale-105 w-56 transition-transform">
|
<div class="hover:scale-105 w-56 transition-transform">
|
||||||
<div
|
<div
|
||||||
class="border-2 credit-transition border-solid border-foreground-muted-alt rounded-2xl overflow-hidden"
|
class="border-2 credit-transition border-solid border-foreground-muted-alt rounded-2xl overflow-hidden"
|
||||||
style="--delay: {i * 50 + multiplier * 8}ms;"
|
style="--delay: {i * 50 + multiplier * 9}ms;"
|
||||||
>
|
>
|
||||||
<a class="w-48" href={credit.url} target="_blank">
|
<a class="w-48" href={credit.url} target="_blank">
|
||||||
<img src={credit.avatar} alt="{credit.name}'s avatar" />
|
<img src={credit.avatar} alt="{credit.name}'s avatar" />
|
||||||
|
@ -101,7 +111,7 @@
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="text-foreground-muted text-base mt-10 text-transition"
|
class="text-foreground-muted text-base mt-10 text-transition"
|
||||||
style="--delay: {9 * multiplier}ms"
|
style="--delay: {10 * multiplier}ms"
|
||||||
>
|
>
|
||||||
(obviously inspired by <a
|
(obviously inspired by <a
|
||||||
href="https://cobalt.tools"
|
href="https://cobalt.tools"
|
||||||
|
|
|
@ -376,7 +376,7 @@
|
||||||
direction={isSm ? "bottom" : "right"}
|
direction={isSm ? "bottom" : "right"}
|
||||||
endIntensity={128}
|
endIntensity={128}
|
||||||
iterations={6}
|
iterations={6}
|
||||||
fadeTo="rgba(255, 255, 255, 0.6)"
|
fadeTo="var(--bg-transparent)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Config } from "tailwindcss";
|
import type { Config } from "tailwindcss";
|
||||||
|
import plugin from "tailwindcss/plugin";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{html,js,svelte,ts}"],
|
content: ["./src/**/*.{html,js,svelte,ts}"],
|
||||||
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
@ -25,5 +25,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [],
|
plugins: [
|
||||||
|
plugin(function ({ addVariant }) {
|
||||||
|
addVariant("dynadark", [
|
||||||
|
"body:not(.light).dark &",
|
||||||
|
"@media (prefers-color-scheme: dark) { body:not(.light) &",
|
||||||
|
]);
|
||||||
|
}),
|
||||||
|
],
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
|
|
@ -26,4 +26,11 @@ export default defineConfig({
|
||||||
"@ffmpeg/util",
|
"@ffmpeg/util",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
scss: {
|
||||||
|
api: "modern",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue