refactor: deduplicate modal/button styles and enforce global utility usage

- Consolidated all modal-related styles into Modal.module.css; ValidationModal.module.css is now deprecated
- All main action/navigation buttons in NewGame and GameDetail use global .btn/.nav-buttons utility classes
- Removed duplicate utility classes from component CSS files
- Fixed .fullscreenToggle class naming for consistency
- Cleaned up component CSS to only contain component-specific styles
- Updated GameCompletionModal to use shared modal styles

This ensures DRY, maintainable, and consistent styling across the app.
This commit is contained in:
Frank Schwenk
2025-06-06 16:42:11 +02:00
parent 209df5d9f2
commit d1379985f3
13 changed files with 51 additions and 202 deletions

View File

@@ -1,3 +1,4 @@
/* FullscreenToggle-specific styles only. */
.fullscreenToggle {
position: fixed;
bottom: 20px;

View File

@@ -1,4 +1,5 @@
import { h } from 'preact';
import modalStyles from './Modal.module.css';
import styles from './GameCompletionModal.module.css';
/**
@@ -21,13 +22,13 @@ const GameCompletionModal = ({ open, game, onConfirm, onClose }) => {
? `Unentschieden zwischen ${winners.join(' und ')}`
: `${winners[0]} hat gewonnen!`;
return (
<div id="game-completion-modal" className="modal show" role="dialog" aria-modal="true" aria-labelledby="completion-modal-title">
<div className={styles['modal-content']}>
<div className={styles['modal-header']}>
<span className={styles['modal-title']} id="completion-modal-title">Spiel beendet</span>
<button className={styles['close-button']} onClick={onClose} aria-label="Schließen">×</button>
<div id="game-completion-modal" className={modalStyles['modal'] + ' ' + modalStyles['show']} role="dialog" aria-modal="true" aria-labelledby="completion-modal-title">
<div className={modalStyles['modal-content']}>
<div className={modalStyles['modal-header']}>
<span className={modalStyles['modal-title']} id="completion-modal-title">Spiel beendet</span>
<button className={modalStyles['close-button']} onClick={onClose} aria-label="Schließen">×</button>
</div>
<div className={styles['modal-body']}>
<div className={modalStyles['modal-body']}>
<div className={styles['final-scores']}>
{playerNames.map((name, idx) => (
<div className={styles['final-score']} key={name + idx}>
@@ -38,7 +39,7 @@ const GameCompletionModal = ({ open, game, onConfirm, onClose }) => {
</div>
<div className={styles['winner-announcement']}><h3>{winnerText}</h3></div>
</div>
<div className={styles['modal-footer']}>
<div className={modalStyles['modal-footer']}>
<button className={styles['btn'] + ' ' + styles['btn--warning']} onClick={onConfirm} aria-label="Bestätigen">Bestätigen</button>
<button className={styles['btn']} onClick={onClose} aria-label="Abbrechen">Abbrechen</button>
</div>

View File

@@ -1,26 +1,4 @@
#game-completion-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.8);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #222;
padding: 32px 24px 24px 24px;
border-radius: 16px;
width: 90vw;
max-width: 480px;
box-shadow: 0 4px 32px rgba(0,0,0,0.7);
position: relative;
margin: auto;
transform: none;
}
/* Only GameCompletionModal-specific styles. Shared modal styles are now in Modal.module.css */
.final-scores {
margin: 20px 0;
}
@@ -60,14 +38,6 @@
margin: 0;
color: #fff;
}
.modal-footer {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 24px;
width: 100%;
justify-content: center;
}
.btn {
flex: 1;
padding: 18px 0;
@@ -89,25 +59,7 @@
.btn--warning:hover {
background: #d32f2f;
}
.close-button {
position: absolute;
top: 12px;
right: 16px;
background: none;
border: none;
color: #aaa;
font-size: 2rem;
cursor: pointer;
z-index: 2;
}
.close-button:hover {
color: #fff;
}
@media (max-width: 600px) {
.modal-content {
padding: 16px 4px 16px 4px;
max-width: 98vw;
}
.btn {
font-size: 1rem;
padding: 14px 0;

View File

@@ -45,8 +45,8 @@ const GameDetail = ({ game, onFinishGame, onUpdateScore, onBack }) => {
))}
</div>
<div className={styles['game-detail-controls']}>
<button className={styles['nav-button']} onClick={onBack} aria-label="Zurück zur Liste">Zurück zur Liste</button>
<button className={styles['nav-button']} disabled={isCompleted} onClick={onFinishGame} aria-label={isCompleted ? 'Abgeschlossen' : 'Spiel beenden'}>{isCompleted ? 'Abgeschlossen' : 'Spiel beenden'}</button>
<button className="btn" onClick={onBack} aria-label="Zurück zur Liste">Zurück zur Liste</button>
<button className="btn" disabled={isCompleted} onClick={onFinishGame} aria-label={isCompleted ? 'Abgeschlossen' : 'Spiel beenden'}>{isCompleted ? 'Abgeschlossen' : 'Spiel beenden'}</button>
</div>
</div>
);

View File

@@ -1,3 +1,4 @@
/* GameDetail-specific styles only. Shared utility classes are now in global CSS. */
.screen {
position: absolute;
top: 0;
@@ -121,22 +122,4 @@
margin: 40px 0 0 0;
width: 100%;
justify-content: center;
}
.nav-button {
flex: 1;
min-width: 200px;
padding: 24px 0;
color: #fff;
background: #333;
border: none;
border-radius: 8px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s, color 0.2s;
margin-bottom: 0;
margin-top: 0;
}
.nav-button:hover:enabled {
background: #444;
}

View File

@@ -1,3 +1,4 @@
/* GameHistory-specific styles only. Shared utility classes are now in global CSS. */
.screen {
position: absolute;
top: 0;
@@ -20,21 +21,4 @@
.screen-title {
font-size: 24px;
margin-bottom: 20px;
}
.nav-buttons {
display: flex;
flex-direction: column;
gap: 10px;
margin: 20px 0 40px 0;
}
.btn {
flex: 1;
min-width: 100px;
padding: 20px;
color: white;
border: none;
border-radius: 0;
font-size: 20px;
cursor: pointer;
touch-action: manipulation;
}

View File

@@ -1,3 +1,4 @@
/* GameList-specific styles only. Shared utility classes are now in global CSS. */
.screen.active {
display: block;
opacity: 1;
@@ -21,23 +22,6 @@
flex: 1;
overflow-y: auto;
}
.nav-buttons {
display: flex;
flex-direction: column;
gap: 10px;
margin: 20px 0 40px 0;
}
.btn {
flex: 1;
min-width: 100px;
padding: 20px;
color: white;
border: none;
border-radius: 0;
font-size: 20px;
cursor: pointer;
touch-action: manipulation;
}
.filter-buttons {
display: flex;
gap: 8px;

View File

@@ -1,3 +1,4 @@
/* Consolidated modal styles for all modals */
.modal {
display: none;
position: fixed;
@@ -31,6 +32,8 @@
font-size: 24px;
cursor: pointer;
color: #888;
background: none;
border: none;
}
.close-button:hover {
color: white;

View File

@@ -60,7 +60,7 @@ const NewGame = ({ onCreateGame, playerNameHistory, onCancel, onGameCreated, ini
<form className={styles['new-game-form']} onSubmit={handleSubmit} aria-label="Neues Spiel Formular">
<div className={styles['screen-title']}>Neues Spiel</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
<button type="button" className={styles['btn']} onClick={handleClear} aria-label="Felder leeren">Felder leeren</button>
<button type="button" className="btn" onClick={handleClear} aria-label="Felder leeren">Felder leeren</button>
</div>
<div className={styles['player-inputs']}>
<div className={styles['player-input']}>
@@ -106,9 +106,9 @@ const NewGame = ({ onCreateGame, playerNameHistory, onCancel, onGameCreated, ini
</div>
</div>
{error && <div className={styles['validation-error']}>{error}</div>}
<div className={styles['nav-buttons']}>
<button type="button" className={styles['btn']} onClick={onCancel} aria-label="Abbrechen">Abbrechen</button>
<button type="submit" className={styles['btn']} aria-label="Spiel starten">Spiel starten</button>
<div className="nav-buttons">
<button type="button" className="btn" onClick={onCancel} aria-label="Abbrechen">Abbrechen</button>
<button type="submit" className="btn" aria-label="Spiel starten">Spiel starten</button>
</div>
</form>
);

View File

@@ -1,3 +1,4 @@
/* NewGame-specific styles only. Shared utility classes are now in global CSS. */
.screen {
position: absolute;
top: 0;
@@ -97,29 +98,6 @@
font-size: 1.1rem;
text-align: center;
}
.nav-buttons {
display: flex;
flex-direction: column;
gap: 12px;
margin: 16px 0 0 0;
}
.btn {
flex: 1;
min-width: 100px;
padding: 18px;
color: white;
background: #333;
border: none;
border-radius: 6px;
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
touch-action: manipulation;
transition: background 0.2s, color 0.2s;
}
.btn:hover {
background: #444;
}
.new-game-form {
width: 100%;
max-width: 700px;

View File

@@ -1,5 +1,5 @@
import { h } from 'preact';
import styles from './ValidationModal.module.css';
import styles from './Modal.module.css';
/**
* Modal for displaying validation errors.

View File

@@ -1,63 +1 @@
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
}
.modal.show {
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #2a2a2a;
padding: 20px;
border-radius: 10px;
width: 90%;
max-width: 500px;
position: relative;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.close-button {
font-size: 24px;
cursor: pointer;
color: #888;
}
.close-button:hover {
color: white;
}
.modal-body {
margin-bottom: 20px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.modal-button {
padding: 8px 16px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.modal-button.cancel {
background-color: #444;
color: white;
}
.modal-button.confirm {
background-color: #e74c3c;
color: white;
}
.modal-button:hover {
opacity: 0.9;
}
/* DEPRECATED: All modal styles are now in Modal.module.css */

View File

@@ -26,7 +26,7 @@ input, select {
/* Responsive adjustments for fullscreen toggle button */
@media screen and (max-width: 480px) {
.fullscreen-toggle {
.fullscreenToggle {
bottom: 15px;
right: 15px;
width: 40px;
@@ -55,6 +55,31 @@ input, select {
background: #333;
}
/* Shared utility classes for buttons and layout */
.btn {
flex: 1;
min-width: 100px;
padding: 18px;
color: white;
background: #333;
border: none;
border-radius: 6px;
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
touch-action: manipulation;
transition: background 0.2s, color 0.2s;
}
.btn:hover {
background: #444;
}
.nav-buttons {
display: flex;
flex-direction: column;
gap: 12px;
margin: 16px 0 0 0;
}
/* Modal overlay (global, not component-specific) */
.modal {
position: fixed;