feat: add themes functionality

This commit is contained in:
madkarmaa 2025-04-05 10:47:11 +02:00
parent c0e75c1116
commit eb51ccfd88
6 changed files with 96 additions and 0 deletions

View File

@ -39,6 +39,7 @@
]
},
"dependencies": {
"moment": "^2.30.1",
"svelte-material-icons": "^3.0.5"
}
}

8
pnpm-lock.yaml generated
View File

@ -8,6 +8,9 @@ importers:
.:
dependencies:
moment:
specifier: ^2.30.1
version: 2.30.1
svelte-material-icons:
specifier: ^3.0.5
version: 3.0.5(svelte@5.25.3)
@ -938,6 +941,9 @@ packages:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
moment@2.30.1:
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@ -2012,6 +2018,8 @@ snapshots:
dependencies:
brace-expansion: 2.0.1
moment@2.30.1: {}
mri@1.2.0: {}
mrmime@2.0.1: {}

21
src/lib/stores.ts Normal file
View File

@ -0,0 +1,21 @@
import { readable } from 'svelte/store';
import moment from 'moment';
import { themes } from './themes';
import { seconds } from './time';
export const theme = readable('default', (set) => {
const updateTheme = () => {
const today = moment();
const monthName = today.format('MMMM').toLowerCase();
const day = today.date();
Object.entries(themes).forEach(([themeName, [month, date]]) => {
if (monthName === month && day === date) set(themeName);
});
};
updateTheme();
const interval = setInterval(updateTheme, seconds(30));
return () => clearInterval(interval);
});

49
src/lib/themes.ts Normal file
View File

@ -0,0 +1,49 @@
import moment, { type Moment } from 'moment';
type MonthName =
| 'january'
| 'february'
| 'march'
| 'april'
| 'may'
| 'june'
| 'july'
| 'august'
| 'september'
| 'october'
| 'november'
| 'december';
const getEaster = (): Moment => {
const currentYear = new Date().getFullYear();
const a = currentYear % 19,
b = Math.floor(currentYear / 100),
c = currentYear % 100,
d = Math.floor(b / 4),
e = b % 4,
f = Math.floor((b + 8) / 25),
g = Math.floor((b - f + 1) / 3),
h = (19 * a + b - d - g + 15) % 30,
i = Math.floor(c / 4),
k = c % 4,
L = (32 + 2 * e + 2 * i - h - k) % 7,
m = Math.floor((a + 11 * h + 22 * L) / 451),
month = Math.floor((h + L - 7 * m + 114) / 31),
day = ((h + L - 7 * m + 114) % 31) + 1;
return moment(new Date(currentYear, month - 1, day));
};
export const themes = {
new_year: ['january', 1],
christmas: ['december', 25],
valentine: ['february', 14],
halloween: ['october', 31],
// prettier-ignore
easter: [
getEaster().format('MMMM').toLowerCase() as MonthName,
getEaster().date()
],
april_fools: ['april', 1]
} as const satisfies Record<string, [month: MonthName, date: number]>;

4
src/lib/time.ts Normal file
View File

@ -0,0 +1,4 @@
export const seconds = (seconds: number): number => seconds * 1000;
export const minutes = (minutes: number): number => seconds(minutes * 60);
export const hours = (hours: number): number => minutes(hours * 60);
export const days = (days: number): number => hours(days * 24);

13
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,13 @@
<script lang="ts">
import { theme } from '$lib/stores';
import { onMount, type Snippet } from 'svelte';
type Props = { children: Snippet };
let { children }: Props = $props();
onMount(() => {
document.documentElement.setAttribute('data-theme', $theme);
});
</script>
{@render children()}