Implement dark-first design system with Geist typography (#28)
All checks were successful
CI / backend-lint (push) Successful in 10s
CI / actions-lint (push) Successful in 16s
CI / frontend-lint (push) Successful in 21s

Co-authored-by: Julian Tabel <juliantabel.jt@gmail.com>
Co-committed-by: Julian Tabel <juliantabel.jt@gmail.com>
This commit was merged in pull request #28.
This commit is contained in:
2026-02-17 20:48:42 +01:00
committed by TheFurya
parent e3b3dc5317
commit 42b66ee9a2
56 changed files with 1151 additions and 1067 deletions

View File

@@ -55,8 +55,7 @@ const statusOptions: {
{
value: 'missed',
label: 'Missed / Ran',
color:
'bg-gray-100 text-gray-800 border-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600',
color: 'bg-surface-2 text-text-primary border-border-default',
},
]
@@ -261,16 +260,13 @@ export function EncounterModal({
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="fixed inset-0 bg-black/50" onClick={onClose} />
<div className="relative bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto">
<div className="sticky top-0 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4 rounded-t-xl">
<div className="relative bg-surface-1 rounded-xl shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto">
<div className="sticky top-0 bg-surface-1 border-b border-border-default px-6 py-4 rounded-t-xl">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
<h2 className="text-lg font-semibold text-text-primary">
{isEditing ? 'Edit Encounter' : 'Log Encounter'}
</h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
>
<button onClick={onClose} className="text-gray-400 hover:text-text-primary">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
@@ -281,7 +277,7 @@ export function EncounterModal({
</svg>
</button>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">{route.name}</p>
<p className="text-sm text-text-tertiary mt-1">{route.name}</p>
</div>
<div className="px-6 py-4 space-y-4">
@@ -289,9 +285,7 @@ export function EncounterModal({
{!isEditing && (
<div>
<div className="flex items-center justify-between mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Pokemon
</label>
<label className="block text-sm font-medium text-text-secondary">Pokemon</label>
{!loadingPokemon && routePokemon && routePokemon.length > 0 && (
<button
type="button"
@@ -325,7 +319,7 @@ export function EncounterModal({
placeholder="Search pokemon..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full px-3 py-1.5 mb-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-1.5 mb-2 rounded-lg border border-border-default bg-surface-2 text-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-accent-400"
/>
)}
{availableConditions.length > 0 && (
@@ -336,7 +330,7 @@ export function EncounterModal({
className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${
selectedCondition === null
? 'bg-purple-100 border-purple-300 text-purple-800 dark:bg-purple-900/40 dark:border-purple-600 dark:text-purple-300'
: 'border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-purple-300 dark:hover:border-purple-600'
: 'border-border-default text-text-tertiary hover:border-purple-300 dark:hover:border-purple-600'
}`}
>
All
@@ -349,7 +343,7 @@ export function EncounterModal({
className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors capitalize ${
selectedCondition === cond
? 'bg-purple-100 border-purple-300 text-purple-800 dark:bg-purple-900/40 dark:border-purple-600 dark:text-purple-300'
: 'border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-purple-300 dark:hover:border-purple-600'
: 'border-border-default text-text-tertiary hover:border-purple-300 dark:hover:border-purple-600'
}`}
>
{cond}
@@ -360,11 +354,9 @@ export function EncounterModal({
<div className="max-h-64 overflow-y-auto space-y-3">
{groupedPokemon.map(({ method, pokemon }, groupIdx) => (
<div key={method}>
{groupIdx > 0 && (
<div className="border-t border-gray-200 dark:border-gray-700 mb-3" />
)}
{groupIdx > 0 && <div className="border-t border-border-default mb-3" />}
{hasMultipleGroups && (
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1.5">
<div className="text-xs font-medium text-text-tertiary mb-1.5">
{getMethodLabel(method)}
</div>
)}
@@ -382,10 +374,10 @@ export function EncounterModal({
disabled={isDuped}
className={`flex flex-col items-center p-2 rounded-lg border text-center transition-colors ${
isDuped
? 'opacity-40 cursor-not-allowed border-gray-200 dark:border-gray-700'
? 'opacity-40 cursor-not-allowed border-border-default'
: isSelected
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30'
: 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
: 'border-border-default hover:border-border-default'
}`}
>
{rp.pokemon.spriteUrl ? (
@@ -395,11 +387,11 @@ export function EncounterModal({
className="w-10 h-10"
/>
) : (
<div className="w-10 h-10 rounded-full bg-gray-200 dark:bg-gray-600 flex items-center justify-center text-xs font-bold">
<div className="w-10 h-10 rounded-full bg-surface-3 flex items-center justify-center text-xs font-bold">
{rp.pokemon.name[0]?.toUpperCase()}
</div>
)}
<span className="text-xs text-gray-700 dark:text-gray-300 mt-1 capitalize">
<span className="text-xs text-text-secondary mt-1 capitalize">
{rp.pokemon.name}
</span>
{isDuped && (
@@ -439,16 +431,14 @@ export function EncounterModal({
</div>
</>
) : (
<p className="text-sm text-gray-500 dark:text-gray-400 py-2">
No pokemon data for this route
</p>
<p className="text-sm text-text-tertiary py-2">No pokemon data for this route</p>
)}
</div>
)}
{/* Editing: show pokemon info */}
{isEditing && existing && (
<div className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-900/50 rounded-lg">
<div className="flex items-center gap-3 p-3 bg-surface-0/50 rounded-lg">
{existing.pokemon.spriteUrl ? (
<img
src={existing.pokemon.spriteUrl}
@@ -456,12 +446,12 @@ export function EncounterModal({
className="w-12 h-12"
/>
) : (
<div className="w-12 h-12 rounded-full bg-gray-200 dark:bg-gray-600 flex items-center justify-center text-lg font-bold">
<div className="w-12 h-12 rounded-full bg-surface-3 flex items-center justify-center text-lg font-bold">
{existing.pokemon.name[0]?.toUpperCase()}
</div>
)}
<div>
<div className="font-medium text-gray-900 dark:text-gray-100 capitalize">
<div className="font-medium text-text-primary capitalize">
{existing.pokemon.name}
</div>
<div className="text-xs text-gray-500">
@@ -473,9 +463,7 @@ export function EncounterModal({
{/* Status */}
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Status
</label>
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
<div className="flex gap-2">
{statusOptions.map((opt) => (
<button
@@ -485,7 +473,7 @@ export function EncounterModal({
className={`flex-1 px-3 py-2 rounded-lg border text-sm font-medium transition-colors ${
status === opt.value
? opt.color
: 'border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:border-gray-300'
: 'border-border-default text-text-tertiary hover:border-gray-300'
}`}
>
{opt.label}
@@ -499,7 +487,7 @@ export function EncounterModal({
<div>
<label
htmlFor="nickname"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
className="block text-sm font-medium text-text-secondary mb-1"
>
Nickname
</label>
@@ -509,19 +497,17 @@ export function EncounterModal({
value={nickname}
onChange={(e) => setNickname(e.target.value)}
placeholder="Give it a name..."
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
/>
{showSuggestions && suggestions && suggestions.length > 0 && (
<div className="mt-2">
<div className="flex items-center justify-between mb-1.5">
<span className="text-xs text-gray-500 dark:text-gray-400">
Suggestions ({namingScheme})
</span>
<span className="text-xs text-text-tertiary">Suggestions ({namingScheme})</span>
<button
type="button"
onClick={() => regenerate()}
disabled={loadingSuggestions}
className="text-xs text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 disabled:opacity-50 transition-colors"
className="text-xs text-text-link hover:text-blue-700 dark:hover:text-blue-300 disabled:opacity-50 transition-colors"
>
{loadingSuggestions ? 'Loading...' : 'Regenerate'}
</button>
@@ -535,7 +521,7 @@ export function EncounterModal({
className={`px-2.5 py-1 text-xs rounded-full border transition-colors ${
nickname === name
? 'bg-blue-100 border-blue-300 text-blue-800 dark:bg-blue-900/40 dark:border-blue-600 dark:text-blue-300'
: 'border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:border-blue-300 dark:hover:border-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20'
: 'border-border-default text-text-secondary hover:border-blue-300 dark:hover:border-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20'
}`}
>
{name}
@@ -552,7 +538,7 @@ export function EncounterModal({
<div>
<label
htmlFor="catch-level"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
className="block text-sm font-medium text-text-secondary mb-1"
>
Catch Level
</label>
@@ -568,7 +554,7 @@ export function EncounterModal({
? `${selectedPokemon.minLevel}${selectedPokemon.maxLevel}`
: 'Level'
}
className="w-24 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-24 px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
/>
</div>
)}
@@ -579,7 +565,7 @@ export function EncounterModal({
<div>
<label
htmlFor="faint-level"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
className="block text-sm font-medium text-text-secondary mb-1"
>
Faint Level <span className="font-normal text-gray-400">(mark as dead)</span>
</label>
@@ -591,13 +577,13 @@ export function EncounterModal({
value={faintLevel}
onChange={(e) => setFaintLevel(e.target.value)}
placeholder="Leave empty if still alive"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
/>
</div>
<div>
<label
htmlFor="death-cause"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
className="block text-sm font-medium text-text-secondary mb-1"
>
Cause of Death <span className="font-normal text-gray-400">(optional)</span>
</label>
@@ -608,18 +594,18 @@ export function EncounterModal({
value={deathCause}
onChange={(e) => setDeathCause(e.target.value)}
placeholder="e.g. Crit from rival's Charizard"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
/>
</div>
</>
)}
</div>
<div className="sticky bottom-0 bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 px-6 py-4 rounded-b-xl flex justify-end gap-3">
<div className="sticky bottom-0 bg-surface-1 border-t border-border-default px-6 py-4 rounded-b-xl flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg font-medium hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
className="px-4 py-2 text-text-secondary bg-surface-2 rounded-lg font-medium hover:bg-surface-3 transition-colors"
>
Cancel
</button>