mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
prompt on win screen, and layout/theme improvements
## New features
- **About page** (`src/routes/about/`): New static about page rendered
from `static/about.md` using the `marked` library (added as a
dependency). Includes the project backstory content.
- **XML sitemap** (`src/routes/sitemap.xml/`): Dynamic sitemap
endpoint for SEO, registered in `static/robots.txt` via `Sitemap:`
directive.
- **Apple Sign In prompt on win screen** (`WinScreen.svelte`): When
the game is won and the user is not logged in, a "Sign in to save
your streak & see your stats" prompt with an Apple Sign In button is
shown below the share card. Passes `anonymousId` so stats migrate on
sign-up. Driven by new `isLoggedIn` and `anonymousId` props, passed
from `+page.svelte`.
## Refactoring
- **`SocialLinks` component**
(`src/lib/components/SocialLinks.svelte`): Extracted the Bluesky,
Twitter/X, and email social link icons from `Credits.svelte` into a
reusable component. `Credits.svelte` now imports and renders
`<SocialLinks />`.
- **`ThemeToggle` component**
(`src/lib/components/ThemeToggle.svelte`): New component for
toggling light/dark mode, persisted to `localStorage`. Currently
rendered but hidden (`hidden` class) in `+page.svelte` —
infrastructure is in place for future use.
## Layout changes
- **`+layout.svelte`**: Moved the page title/header (`<h1>` with
`TitleAnimation`) and the gradient background wrapper from
`+page.svelte` into the root layout, so it applies across all
routes. Also removed the `browser` guard around the analytics script
injection (it's
already inside `onMount` which is client-only). Added `<meta
name="description">`.
- **`+page.svelte`**: Removed the title/header and gradient wrapper
(now in layout). Minor formatting cleanup (reformatted `SearchInput`
props, moved `currentDate` derived state earlier). `ThemeToggle`
import swapped in place of `TitleAnimation` (which moved to layout).
## Styling
- **`layout.css`**: Added `@custom-variant dark` for class-based dark
mode toggling (supports `.dark` class on `<html>`). Added explicit
`html.dark` / `html.light` rules alongside the existing
`prefers-color-scheme` media query, so the `ThemeToggle` component
can
override the system preference. Added background transition
animation.
63 lines
2.0 KiB
Svelte
63 lines
2.0 KiB
Svelte
<script lang="ts">
|
|
import { browser } from '$app/environment';
|
|
import { onMount } from 'svelte';
|
|
|
|
let isDarkMode = $state(false);
|
|
|
|
onMount(() => {
|
|
const stored = localStorage.getItem('bibdle-theme');
|
|
if (stored === 'dark') {
|
|
isDarkMode = true;
|
|
} else if (stored === 'light') {
|
|
isDarkMode = false;
|
|
} else {
|
|
isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
}
|
|
});
|
|
|
|
$effect(() => {
|
|
if (isDarkMode) {
|
|
document.documentElement.classList.add('dark');
|
|
document.documentElement.classList.remove('light');
|
|
localStorage.setItem('bibdle-theme', 'dark');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
document.documentElement.classList.add('light');
|
|
localStorage.setItem('bibdle-theme', 'light');
|
|
}
|
|
});
|
|
|
|
function toggleTheme() {
|
|
isDarkMode = !isDarkMode;
|
|
}
|
|
</script>
|
|
|
|
{#if browser}
|
|
<button
|
|
onclick={toggleTheme}
|
|
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
|
|
class="flex items-center gap-2 p-1 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100 transition-colors duration-200 cursor-pointer"
|
|
>
|
|
{#if isDarkMode}
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="4"/>
|
|
<path d="M12 2v2"/>
|
|
<path d="M12 20v2"/>
|
|
<path d="m4.93 4.93 1.41 1.41"/>
|
|
<path d="m17.66 17.66 1.41 1.41"/>
|
|
<path d="M2 12h2"/>
|
|
<path d="M20 12h2"/>
|
|
<path d="m6.34 17.66-1.41 1.41"/>
|
|
<path d="m19.07 4.93-1.41 1.41"/>
|
|
</svg>
|
|
{:else}
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>
|
|
</svg>
|
|
{/if}
|
|
<span class="text-xs uppercase tracking-widest">
|
|
{isDarkMode ? 'Light mode' : 'Dark mode'}
|
|
</span>
|
|
</button>
|
|
{/if}
|