mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-02-04 10:54:44 -05:00
major styling and spacing
This commit is contained in:
16
src/lib/components/Container.svelte
Normal file
16
src/lib/components/Container.svelte
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: Snippet;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children, class: className = "" }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="inline-flex flex-col items-center bg-white/50 backdrop-blur-sm rounded-2xl border border-white/50 shadow-sm {className}"
|
||||||
|
>
|
||||||
|
{@render children()}
|
||||||
|
</div>
|
||||||
@@ -74,13 +74,11 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="text-center py-12">
|
<div class="w-full">
|
||||||
<div
|
<div
|
||||||
class="inline-flex flex-col items-center bg-white/50 backdrop-blur-sm px-8 py-4 rounded-2xl border border-white/50 shadow-sm"
|
class="flex flex-col items-center bg-white/50 backdrop-blur-sm px-8 py-4 rounded-2xl border border-white/50 shadow-sm w-full"
|
||||||
>
|
|
||||||
<p
|
|
||||||
class="text-xs uppercase tracking-[0.2em] text-gray-500 font-bold mb-2"
|
|
||||||
>
|
>
|
||||||
|
<p class="text-xs uppercase tracking-[0.2em] text-gray-500 font-bold mb-2">
|
||||||
Next Verse In
|
Next Verse In
|
||||||
</p>
|
</p>
|
||||||
<p class="text-4xl font-triodion font-black text-gray-800 tabular-nums">
|
<p class="text-4xl font-triodion font-black text-gray-800 tabular-nums">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
>
|
>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div class="text-center py-12">
|
<div class="text-center" in:fade={{ delay: 1500, duration: 1000 }}>
|
||||||
<div
|
<div
|
||||||
class="inline-flex w-full flex-col items-center bg-white/50 backdrop-blur-sm px-8 py-4 rounded-2xl border border-white/50 shadow-sm"
|
class="inline-flex w-full flex-col items-center bg-white/50 backdrop-blur-sm px-8 py-4 rounded-2xl border border-white/50 shadow-sm"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
let filteredBooks = $derived(
|
let filteredBooks = $derived(
|
||||||
bibleBooks.filter((book) =>
|
bibleBooks.filter((book) =>
|
||||||
book.name.toLowerCase().includes(searchQuery.toLowerCase()),
|
book.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleKeydown(e: KeyboardEvent) {
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-12">
|
<div>
|
||||||
<input
|
<input
|
||||||
bind:value={searchQuery}
|
bind:value={searchQuery}
|
||||||
placeholder="Type to guess a book (e.g. 'Genesis', 'John')..."
|
placeholder="Type to guess a book (e.g. 'Genesis', 'John')..."
|
||||||
@@ -30,9 +30,7 @@
|
|||||||
{#each filteredBooks as book (book.id)}
|
{#each filteredBooks as book (book.id)}
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
class="w-full p-4 sm:p-5 text-left {guessedIds.has(
|
class="w-full p-4 sm:p-5 text-left {guessedIds.has(book.id)
|
||||||
book.id,
|
|
||||||
)
|
|
||||||
? 'opacity-60 cursor-not-allowed pointer-events-none hover:bg-gray-100 hover:text-gray-600'
|
? 'opacity-60 cursor-not-allowed pointer-events-none hover:bg-gray-100 hover:text-gray-600'
|
||||||
: 'hover:bg-blue-50 hover:text-blue-700'} transition-all border-b border-gray-100 last:border-b-0 flex items-center"
|
: 'hover:bg-blue-50 hover:text-blue-700'} transition-all border-b border-gray-100 last:border-b-0 flex items-center"
|
||||||
onclick={() => submitGuess(book.id)}
|
onclick={() => submitGuess(book.id)}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageData } from "../../routes/$types.js"; // Approximate type; adjust if needed
|
import type { PageData } from "../../routes/$types.js"; // Approximate type; adjust if needed
|
||||||
|
import Container from "./Container.svelte";
|
||||||
|
|
||||||
let { data, isWon }: { data: PageData; isWon: boolean } = $props();
|
let { data, isWon }: { data: PageData; isWon: boolean } = $props();
|
||||||
let dailyVerse = $derived(data.dailyVerse);
|
let dailyVerse = $derived(data.dailyVerse);
|
||||||
@@ -11,15 +12,17 @@
|
|||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="bg-gray-50 rounded-2xl shadow-xl p-8 sm:p-12 mb-4 sm:mb-12 w-full">
|
<Container class="w-full p-8 sm:p-12">
|
||||||
<blockquote
|
<blockquote
|
||||||
class="text-xl sm:text-2xl font-triodion leading-relaxed text-gray-700 text-center"
|
class="text-xl sm:text-2xl font-triodion leading-relaxed text-gray-700 text-center"
|
||||||
>
|
>
|
||||||
{displayVerseText}
|
{displayVerseText}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
{#if isWon}
|
{#if isWon}
|
||||||
<p class="text-center text-lg! big-text text-green-600! font-bold mt-8">
|
<p
|
||||||
|
class="text-center text-lg! big-text text-green-600! font-bold mt-8 bg-white/70 rounded-xl px-4 py-2"
|
||||||
|
>
|
||||||
{displayReference}
|
{displayReference}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</Container>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { getBookById, toOrdinal, getNextGradeMessage } from "$lib/utils/game";
|
import { getBookById, toOrdinal, getNextGradeMessage } from "$lib/utils/game";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import Container from "./Container.svelte";
|
||||||
|
import CountdownTimer from "./CountdownTimer.svelte";
|
||||||
|
|
||||||
interface StatsData {
|
interface StatsData {
|
||||||
solveRank: number;
|
solveRank: number;
|
||||||
@@ -36,7 +38,7 @@
|
|||||||
const congratulationsMessages: WeightedMessage[] = [
|
const congratulationsMessages: WeightedMessage[] = [
|
||||||
{ text: "Congratulations!", weight: 10 },
|
{ text: "Congratulations!", weight: 10 },
|
||||||
{ text: "You got it!", weight: 1000 },
|
{ text: "You got it!", weight: 1000 },
|
||||||
{ text: "Yup,", weight: 100 },
|
{ text: "Yup.", weight: 100 },
|
||||||
{ text: "Very nice!", weight: 1 },
|
{ text: "Very nice!", weight: 1 },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -73,27 +75,27 @@
|
|||||||
let congratulationsMessage = $derived(getRandomCongratulationsMessage());
|
let congratulationsMessage = $derived(getRandomCongratulationsMessage());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="flex flex-col gap-6">
|
||||||
class="p-8 sm:p-12 w-full bg-linear-to-r from-green-400 to-green-600 text-white rounded-2xl shadow-2xl text-center fade-in"
|
<Container
|
||||||
|
class="w-full p-8 sm:p-12 bg-linear-to-r from-green-400/10 to-green-600/30 text-gray-800 shadow-2xl text-center fade-in"
|
||||||
>
|
>
|
||||||
<!-- <h2 class="text-2xl sm:text-4xl font-black mb-4 drop-shadow-lg">
|
<p class="text-2xl sm:text-3xl md:text-4xl leading-relaxed">
|
||||||
{congratulationsMessage}
|
|
||||||
</h2> -->
|
|
||||||
<p class="text-xl sm:text-3xl md:text-4xl">
|
|
||||||
{congratulationsMessage} The verse is from
|
{congratulationsMessage} The verse is from
|
||||||
<span class="font-black text-xl sm:text-2xl md:text-3xl">{bookName}</span>.
|
<span class="font-black text-3xl md:text-4xl">{bookName}</span>.
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p class="text-lg sm:text-xl md:text-2xl mt-4">
|
||||||
class="text-2xl font-bold mt-6 p-2 mx-2 bg-black/20 rounded-lg inline-block"
|
You guessed correctly after {guessCount}
|
||||||
>
|
{guessCount === 1 ? "guess" : "guesses"}.
|
||||||
Your grade: {grade}
|
<span class="font-bold bg-white/40 rounded px-1.5 py-0.75">{grade}</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div class="flex justify-center mt-6">
|
||||||
{#if hasWebShare}
|
{#if hasWebShare}
|
||||||
|
<!-- mobile and arc in production -->
|
||||||
<button
|
<button
|
||||||
onclick={handleShare}
|
onclick={handleShare}
|
||||||
data-umami-event="Share"
|
data-umami-event="Share"
|
||||||
class="mt-4 text-2xl font-bold p-2 bg-white/20 hover:bg-white/30 rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none"
|
class="text-2xl font-bold p-4 bg-white/70 hover:bg-white/80 rounded-xl inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none"
|
||||||
>
|
>
|
||||||
📤 Share
|
📤 Share
|
||||||
</button>
|
</button>
|
||||||
@@ -106,42 +108,48 @@
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
}}
|
}}
|
||||||
data-umami-event="Copy to Clipboard"
|
data-umami-event="Copy to Clipboard"
|
||||||
class={`mt-4 text-2xl font-bold p-2 rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none ${
|
class={`text-2xl font-bold p-4 rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none ${
|
||||||
copySuccess
|
copySuccess ? "bg-white/30" : "bg-white/70 hover:bg-white/80"
|
||||||
? "bg-green-400/50 hover:bg-green-500/60"
|
|
||||||
: "bg-white/20 hover:bg-white/30"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{copySuccess ? "✅ Copied!" : "📋 Copy to clipboard"}
|
{copySuccess ? "✅ Copied!" : "📋 Copy"}
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
|
<!-- dev mode and desktop browsers -->
|
||||||
<button
|
<button
|
||||||
onclick={handleShare}
|
onclick={handleShare}
|
||||||
data-umami-event="Share"
|
data-umami-event="Copy to Clipboard"
|
||||||
class={`mt-4 text-2xl font-bold p-2 ${
|
class={`text-2xl font-bold p-4 ${
|
||||||
copied
|
copied ? "bg-white/30" : "bg-white/70 hover:bg-white/80"
|
||||||
? "bg-green-400/50 hover:bg-green-500/60"
|
} rounded-xl inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none`}
|
||||||
: "bg-white/20 hover:bg-white/30"
|
|
||||||
} rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none`}
|
|
||||||
>
|
>
|
||||||
{copied ? "Copied to clipboard!" : "📤 Share"}
|
{copied ? "✅ Copied!" : "📋 Share"}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="pt-6 big-text text-gray-100!">
|
<p class="pt-6 big-text text-gray-700!">
|
||||||
{getNextGradeMessage(guessCount)}
|
{getNextGradeMessage(guessCount)}
|
||||||
</p>
|
</p>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<CountdownTimer />
|
||||||
|
|
||||||
<!-- Statistics Display -->
|
<!-- Statistics Display -->
|
||||||
{#if statsData}
|
{#if statsData}
|
||||||
<div class="mt-6" in:fade={{ delay: 800 }}>
|
<Container
|
||||||
<div class="grid grid-cols-3 gap-4 gap-x-8 text-center">
|
class="w-full p-4 bg-white/50 backdrop-blur-sm text-gray-800 shadow-lg text-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-3 gap-4 gap-x-8 text-center"
|
||||||
|
in:fade={{ delay: 800 }}
|
||||||
|
>
|
||||||
<!-- Solve Rank Column -->
|
<!-- Solve Rank Column -->
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="text-3xl sm:text-4xl font-black">
|
<div class="text-3xl sm:text-4xl font-black">
|
||||||
#{statsData.solveRank}
|
#{statsData.solveRank}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs sm:text-sm opacity-90 mt-1">
|
<div class="text-sm sm:text-sm opacity-90 mt-1">
|
||||||
You were the {toOrdinal(statsData.solveRank)} person to solve today
|
You were the {toOrdinal(statsData.solveRank)} person to solve today
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -155,7 +163,7 @@
|
|||||||
100
|
100
|
||||||
)}%
|
)}%
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs sm:text-sm opacity-90 mt-1">
|
<div class="text-sm sm:text-sm opacity-90 mt-1">
|
||||||
You ranked {toOrdinal(statsData.guessRank)} of {statsData.totalSolves}
|
You ranked {toOrdinal(statsData.guessRank)} of {statsData.totalSolves}
|
||||||
total solves
|
total solves
|
||||||
</div>
|
</div>
|
||||||
@@ -166,15 +174,19 @@
|
|||||||
<div class="text-3xl sm:text-4xl font-black">
|
<div class="text-3xl sm:text-4xl font-black">
|
||||||
{statsData.averageGuesses}
|
{statsData.averageGuesses}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs sm:text-sm opacity-90 mt-1">
|
<div class="text-sm sm:text-sm opacity-90 mt-1">
|
||||||
People guessed correctly after {statsData.averageGuesses}
|
People guessed correctly after {statsData.averageGuesses}
|
||||||
{statsData.averageGuesses === 1 ? "guess" : "guesses"} on average
|
{statsData.averageGuesses === 1 ? "guess" : "guesses"} on average
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
{:else if !statsSubmitted}
|
{:else if !statsSubmitted}
|
||||||
<div class="mt-6 text-sm opacity-80">Submitting stats...</div>
|
<Container
|
||||||
|
class="w-full p-6 bg-white/50 backdrop-blur-sm text-gray-800 shadow-lg text-center"
|
||||||
|
>
|
||||||
|
<div class="text-sm opacity-80">Submitting stats...</div>
|
||||||
|
</Container>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
import VerseDisplay from "$lib/components/VerseDisplay.svelte";
|
import VerseDisplay from "$lib/components/VerseDisplay.svelte";
|
||||||
import SearchInput from "$lib/components/SearchInput.svelte";
|
import SearchInput from "$lib/components/SearchInput.svelte";
|
||||||
import GuessesTable from "$lib/components/GuessesTable.svelte";
|
import GuessesTable from "$lib/components/GuessesTable.svelte";
|
||||||
import CountdownTimer from "$lib/components/CountdownTimer.svelte";
|
|
||||||
import WinScreen from "$lib/components/WinScreen.svelte";
|
import WinScreen from "$lib/components/WinScreen.svelte";
|
||||||
import Feedback from "$lib/components/Feedback.svelte";
|
import Feedback from "$lib/components/Feedback.svelte";
|
||||||
import TitleAnimation from "$lib/components/TitleAnimation.svelte";
|
import TitleAnimation from "$lib/components/TitleAnimation.svelte";
|
||||||
@@ -408,6 +407,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-6">
|
||||||
<VerseDisplay {data} {isWon} />
|
<VerseDisplay {data} {isWon} />
|
||||||
|
|
||||||
{#if !isWon}
|
{#if !isWon}
|
||||||
@@ -423,13 +423,14 @@
|
|||||||
{statsSubmitted}
|
{statsSubmitted}
|
||||||
guessCount={guesses.length}
|
guessCount={guesses.length}
|
||||||
/>
|
/>
|
||||||
<CountdownTimer />
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<GuessesTable {guesses} {correctBookId} />
|
<GuessesTable {guesses} {correctBookId} />
|
||||||
|
|
||||||
{#if isWon}
|
{#if isWon}
|
||||||
<Feedback />
|
<Feedback />
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
{#if isDev}
|
{#if isDev}
|
||||||
<button
|
<button
|
||||||
onclick={clearLocalStorage}
|
onclick={clearLocalStorage}
|
||||||
|
|||||||
9
todo.md
9
todo.md
@@ -1,6 +1,9 @@
|
|||||||
# in progress
|
# in progress
|
||||||
|
|
||||||
- root menu: classic / imposter mode / impossible mode (complete today's classic and imposter modes to unlock)
|
- Show new/old testament after 3 guesses and section after 7 guesses
|
||||||
|
- Verses ending in semicolons, commas, etc. will be replaced with "..."
|
||||||
|
- For bonus points: guess the verse/psalm number
|
||||||
|
- How do you balance rewarding knowledge vs incentivising learning?
|
||||||
|
|
||||||
# todo
|
# todo
|
||||||
|
|
||||||
@@ -14,8 +17,12 @@
|
|||||||
|
|
||||||
- classic mode: identify what book the verse is from (e.g. Genesis, John, Revelations...) in as few guesses as possible.
|
- classic mode: identify what book the verse is from (e.g. Genesis, John, Revelations...) in as few guesses as possible.
|
||||||
- imposter mode: out of four options, identify the verse that is not in the Bible
|
- imposter mode: out of four options, identify the verse that is not in the Bible
|
||||||
|
- OR out of four options, identify the verse that is not in the same book as the other three options
|
||||||
|
- OR, out of four options, drag them into the
|
||||||
- impossible mode: identify which book of the bible the verse is from in less than three guesses.
|
- impossible mode: identify which book of the bible the verse is from in less than three guesses.
|
||||||
|
|
||||||
|
- The gambling aspect of hoping you get a verse you already know is VERY strong
|
||||||
|
|
||||||
- add login + saved stats + streak etc.
|
- add login + saved stats + streak etc.
|
||||||
|
|
||||||
- add deuterocanonical books
|
- add deuterocanonical books
|
||||||
|
|||||||
Reference in New Issue
Block a user