7 Commits

Author SHA1 Message Date
Frank Schwenk
634d012097 fix(ui): prevent winner modal icons from clipping
Increase top padding, set overflow visible, and position decorative icons inside container so emojis are fully visible.

Refs #26
2025-10-30 10:50:24 +01:00
Frank Schwenk
301d5b131c feat(ux): auto-advance on game type and race selection
- Step 4: selecting a game type immediately advances to step 5
- Step 5: selecting Endlos or a quick-pick number immediately finalizes race-to

Improves flow by removing an extra click. No changes to validation.

Refs #26
2025-10-30 10:41:38 +01:00
Frank Schwenk
4c8b0cfed7 refactor(css): remove global :focus outline styles in index.css\n\nFocus rings were visually distracting for this app context; removed the global *:focus rule. Component-level focus where needed can be handled locally.\n\nRefs #26 2025-10-30 10:36:47 +01:00
Frank Schwenk
31ed600c97 fix(build): remove stray CSS brace causing esbuild minify warning
Fix unexpected '}' in src/styles/index.css after .btn--secondary:hover.\nEliminates Vite/esbuild css-syntax-error during production build.\n\nRefs #26
2025-10-30 10:34:30 +01:00
Frank Schwenk
d016868ff2 feat(ux): autofocus name inputs on new game steps\n\nFocus Player 1/2/3 inputs on mount and place caret at end for faster entry.\n\nNo behavior changes beyond focus; adheres to accessibility with native focus.\n\nRefs #26 2025-10-30 10:15:30 +01:00
Frank Schwenk
89300bc021 fix(ui): align delete icon inline in game list
Apply game-item grid layout to game list rows and switch to two-column layout (1fr auto) so the delete action has a fixed-width slot on the right.

Keeps existing delete button and accessibility attributes; prevents layout stretch.
No behavioral changes beyond layout; click targets unchanged.

Refs #26
2025-10-30 10:04:11 +01:00
Frank Schwenk
de502741e7 Merge github/main into main (resolve .gitea to upstream) 2025-10-30 09:44:14 +01:00
6 changed files with 49 additions and 14 deletions

2
.gitea
View File

@@ -1 +1 @@
https://gitea.schwenk.online/froxxxy/bscscore/issues/26
https://gitea.schwenk.online/froxxxy/bscscore/issues/26

View File

@@ -26,7 +26,7 @@
.winner-announcement {
text-align: center;
margin: 20px 0 0 0;
padding: 24px 16px;
padding: 32px 16px 24px 16px; /* extra top padding to keep icons inside */
background: linear-gradient(135deg, #ff9800 0%, #ffa726 100%);
border-radius: 16px;
font-size: 1.2rem;
@@ -35,13 +35,13 @@
box-shadow: 0 8px 32px rgba(255, 152, 0, 0.3);
animation: celebrationPulse 2s ease-in-out infinite;
position: relative;
overflow: hidden;
overflow: visible; /* avoid clipping decorative icons */
}
.winner-announcement::before {
content: '🎉';
position: absolute;
top: -10px;
top: 6px;
left: 20px;
font-size: 24px;
animation: bounce 1s ease-in-out infinite;
@@ -50,7 +50,7 @@
.winner-announcement::after {
content: '🏆';
position: absolute;
top: -10px;
top: 6px;
right: 20px;
font-size: 24px;
animation: bounce 1s ease-in-out infinite 0.5s;

View File

@@ -76,7 +76,7 @@
/* Game item with better symmetry and spacing */
.game-item {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-columns: 1fr auto;
align-items: center;
gap: var(--space-md);
padding: var(--space-lg);

View File

@@ -82,7 +82,9 @@ export default function GameList({
<Card
key={game.id}
variant="elevated"
className={game.status === 'completed' ? styles['completed'] : styles['active']}
className={
styles['game-item'] + ' ' + (game.status === 'completed' ? styles['completed'] : styles['active'])
}
>
<div
className={styles['game-info']}

View File

@@ -56,6 +56,17 @@ const Player1Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' }:
const [isModalOpen, setIsModalOpen] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
const el = inputRef.current;
if (el) {
el.focus();
const end = el.value.length;
try {
el.setSelectionRange(end, end);
} catch {}
}
}, []);
useEffect(() => {
if (!player1) {
setFilteredNames(playerNameHistory);
@@ -296,6 +307,17 @@ const Player2Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' }:
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
const el = inputRef.current;
if (el) {
el.focus();
const end = el.value.length;
try {
el.setSelectionRange(end, end);
} catch {}
}
}, []);
useEffect(() => {
if (!player2) {
setFilteredNames(playerNameHistory);
@@ -430,6 +452,17 @@ const Player3Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' }:
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
const el = inputRef.current;
if (el) {
el.focus();
const end = el.value.length;
try {
el.setSelectionRange(end, end);
} catch {}
}
}, []);
useEffect(() => {
if (!player3) {
setFilteredNames(playerNameHistory);
@@ -579,6 +612,8 @@ const GameTypeStep = ({ onNext, onCancel, initialValue = '' }: GameTypeStepProps
const handleSelect = (selectedType: string) => {
setGameType(selectedType);
// Auto-advance to next step on selection
onNext(selectedType);
};
const handleSubmit = (e: Event) => {
@@ -673,7 +708,11 @@ const RaceToStep = ({ onNext, onCancel, initialValue = '', gameType }: RaceToSte
const handleQuickPick = (value: number) => {
// For endlos (endless) games, use Infinity to prevent automatic completion
setRaceTo(value === 0 ? 'Infinity' : value);
const selected = value === 0 ? 'Infinity' : value;
setRaceTo(selected);
// Auto-advance to the next step (finalize) when a quick pick is chosen
const raceToValue = selected === 'Infinity' ? Infinity : (parseInt(String(selected), 10) || 0);
onNext(raceToValue);
};
const handleInputChange = (e: Event) => {

View File

@@ -161,7 +161,6 @@ input:focus, select:focus {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
}
.btn-primary:hover {
background: var(--color-primary-hover);
@@ -267,11 +266,6 @@ input:focus, select:focus {
border: 0;
}
/* Focus styles for better accessibility */
*:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Skip link for keyboard navigation */
.skip-link {