Skip to content

Commit 864ada2

Browse files
committed
preview all themes
1 parent 8de17db commit 864ada2

3 files changed

Lines changed: 131 additions & 28 deletions

File tree

src/routes/+page.svelte

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,71 @@
11
<script lang="ts">
2-
import type { PageData } from './$types';
32
import { config } from '../../moire.config';
4-
import { onMount } from 'svelte';
5-
6-
let { data }: { data: PageData } = $props();
7-
let ThemeComponent = $state<any>(null);
8-
9-
$effect(() => {
10-
const theme = config.theme || 'receipt';
11-
// Dynamic import based on theme name
12-
// We need to map string to import path
13-
const themes: Record<string, () => Promise<any>> = {
14-
'receipt': () => import('$themes/receipt/index.svelte'),
15-
'cyberpunk': () => import('$themes/cyberpunk/index.svelte'),
16-
'academic': () => import('$themes/academic/index.svelte'),
17-
'bento': () => import('$themes/bento/index.svelte'),
18-
'pixel': () => import('$themes/pixel/index.svelte')
19-
};
3+
import favicon from '$lib/assets/favicon.svg';
204
21-
const loader = themes[theme] || themes['receipt'];
22-
loader().then(module => {
23-
ThemeComponent = module.default;
24-
});
25-
});
5+
const themes = ['receipt', 'cyberpunk', 'academic', 'bento', 'pixel'];
266
</script>
277

28-
{#if ThemeComponent}
29-
<ThemeComponent {data} {config} />
30-
{:else}
31-
<!-- Simple loading state to avoid flash -->
32-
<div class="min-h-screen flex items-center justify-center">Loading...</div>
33-
{/if}
8+
<!-- Global page styles wrapper -->
9+
<div class="min-h-screen bg-[#111] text-white font-sans antialiased selection:bg-white selection:text-black">
10+
<!-- Moire background pattern -->
11+
<div class="fixed inset-0 -z-10 opacity-40 pointer-events-none moire-bg"></div>
12+
13+
<div class="max-w-[1600px] mx-auto px-4 sm:px-8 py-8 sm:py-16">
14+
<header class="mb-12 sm:mb-24 text-center relative">
15+
<div class="inline-flex items-center gap-4 sm:gap-8 border-[3px] sm:border-4 border-white px-8 sm:px-16 py-4 sm:py-8 mb-4 md:mb-8 bg-black relative">
16+
<!-- Retro shadow effect - made with absolute positioning -->
17+
<div class="absolute -bottom-1.5 sm:-bottom-2 -right-1.5 sm:-right-2 w-full h-full border-[3px] sm:border-4 border-[#444] -z-10 bg-transparent"></div>
18+
19+
<img src={favicon} alt="Moire Logo" class="w-12 h-12 sm:w-20 sm:h-20 invert" />
20+
<div class="text-left">
21+
<h1 class="text-[1.5rem] sm:text-[3rem] md:text-[5rem] font-black leading-none tracking-[0.1em] sm:tracking-[0.2em] uppercase m-0">MOIRE</h1>
22+
<div class="text-[0.7rem] sm:text-sm md:text-base font-mono tracking-[0.3em] sm:tracking-[0.5em] text-[#888] mt-1 sm:mt-2 pl-[0.2em] sm:pl-[0.3em]">THEMES COLLECTION</div>
23+
</div>
24+
</div>
25+
</header>
26+
27+
<div class="grid grid-cols-[repeat(auto-fill,minmax(min(380px,100%),1fr))] gap-6 sm:gap-8">
28+
{#each themes as theme}
29+
<div class="group relative bg-[#1a1a1a] border border-[#333] rounded overflow-hidden transition-all duration-300 flex flex-col h-[600px] sm:h-[700px] hover:-translate-y-2 hover:shadow-[0_20px_40px_rgba(0,0,0,0.5)] hover:border-[#666] hover:z-10">
30+
<div class="px-6 py-4 border-b border-[#333] flex justify-between items-center bg-[#222] transition-colors duration-300 group-hover:bg-white">
31+
<h2 class="m-0 text-sm uppercase tracking-[0.15em] font-bold text-[#888] transition-colors duration-300 group-hover:text-black">{theme}</h2>
32+
<a
33+
href="/examples/{theme}"
34+
target="_blank"
35+
rel="noopener noreferrer"
36+
class="text-xs text-[#888] no-underline px-2.5 py-1 border border-[#444] rounded-full transition-all duration-200 uppercase tracking-wide hover:bg-black hover:text-white hover:border-black group-hover:border-[#ddd] group-hover:text-[#666] group-hover:hover:bg-black group-hover:hover:text-white group-hover:hover:border-black"
37+
>
38+
Open ↗
39+
</a>
40+
</div>
41+
<div class="flex-1 relative bg-black">
42+
<div class="absolute inset-0 pointer-events-none shadow-[inset_0_0_20px_rgba(0,0,0,0.5)] z-10 transition-opacity duration-300 group-hover:opacity-0"></div>
43+
<iframe
44+
src="/examples/{theme}"
45+
title="Theme: {theme}"
46+
loading="lazy"
47+
class="w-full h-full border-none opacity-90 transition-opacity duration-300 group-hover:opacity-100"
48+
></iframe>
49+
</div>
50+
</div>
51+
{/each}
52+
</div>
53+
</div>
54+
</div>
55+
56+
<style>
57+
/*
58+
Complex background patterns are often cleaner in CSS than
59+
arbitrary Tailwind values, but fit the utility-first mindset
60+
by being a Single Purpose Class.
61+
*/
62+
.moire-bg {
63+
background-image:
64+
radial-gradient(#333 1px, transparent 1px),
65+
radial-gradient(#333 1px, transparent 1px);
66+
background-position: 0 0, 10px 10px;
67+
background-size: 20px 20px;
68+
mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
69+
-webkit-mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
70+
}
71+
</style>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
import type { PageServerLoad } from './$types';
3+
import { getMemos } from '$lib/server/memos';
4+
5+
export const load: PageServerLoad = async () => {
6+
const memos = await getMemos();
7+
8+
return {
9+
memos
10+
};
11+
};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<script lang="ts">
2+
import { page } from '$app/stores';
3+
import type { PageData } from './$types';
4+
import { config as baseConfig } from '../../../../moire.config';
5+
6+
let { data }: { data: PageData } = $props();
7+
let ThemeComponent = $state<any>(null);
8+
9+
// Get theme from URL params
10+
const themeName = $derived($page.params.theme);
11+
12+
// Create specific config for this theme instance
13+
const config = $derived({
14+
...baseConfig,
15+
theme: themeName
16+
});
17+
18+
$effect(() => {
19+
const themes: Record<string, () => Promise<any>> = {
20+
'receipt': () => import('$themes/receipt/index.svelte'),
21+
'cyberpunk': () => import('$themes/cyberpunk/index.svelte'),
22+
'academic': () => import('$themes/academic/index.svelte'),
23+
'bento': () => import('$themes/bento/index.svelte'),
24+
'pixel': () => import('$themes/pixel/index.svelte')
25+
};
26+
27+
const loader = themes[themeName];
28+
29+
if (loader) {
30+
loader().then(module => {
31+
ThemeComponent = module.default;
32+
});
33+
}
34+
35+
// Override body class from root layout
36+
const baseTheme = baseConfig.theme || 'receipt';
37+
38+
document.body.classList.remove(baseTheme);
39+
document.body.classList.add(themeName);
40+
41+
return () => {
42+
document.body.classList.remove(themeName);
43+
document.body.classList.add(baseTheme);
44+
};
45+
});
46+
</script>
47+
48+
{#if ThemeComponent}
49+
<ThemeComponent {data} {config} />
50+
{:else}
51+
<div class="h-screen w-full flex items-center justify-center bg-gray-100 text-gray-500">
52+
Loading {themeName}...
53+
</div>
54+
{/if}

0 commit comments

Comments
 (0)