Implement dark-first design system with Geist typography (#28)
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user