Centralize duplicated card shell, press animation, and color tokens

Extract the rounded-card shell and button press animation into a shared
CSS module composed by PageFrame, TitleScreen, and all buttons, add color
tokens for repeated values, and load the saved game once on mount.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 00:23:36 -05:00
parent 3b6bd6815d
commit e0a6aed4cf
7 changed files with 49 additions and 59 deletions
+2 -4
View File
@@ -21,10 +21,8 @@ function clearGame() {
} }
export default function App() { export default function App() {
const [page, setPage] = useState<Page>(() => const [game, setGame] = useState<GameState | null>(loadGame)
loadGame() ? 'scoresheet' : 'title' const [page, setPage] = useState<Page>(() => (game ? 'scoresheet' : 'title'))
)
const [game, setGame] = useState<GameState | null>(() => loadGame())
function handleNewGame() { function handleNewGame() {
clearGame() clearGame()
+5 -11
View File
@@ -1,12 +1,6 @@
.frame { .frame {
position: fixed; composes: card from '../styles/shared.module.css';
inset: 1rem; background: var(--color-parchment);
border: 2px solid black;
border-radius: 2rem;
background: #f5f0e4;
display: flex;
flex-direction: column;
overflow: hidden;
} }
.header { .header {
@@ -23,7 +17,7 @@
.rule { .rule {
position: relative; position: relative;
border: none; border: none;
border-top: 1px solid #c8b99a; border-top: 1px solid var(--color-parchment-border);
margin: 0.75rem 0 0; margin: 0.75rem 0 0;
} }
@@ -33,10 +27,10 @@
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background: #f5f0e4; background: var(--color-parchment);
padding: 0 0.35em; padding: 0 0.35em;
font-size: 0.55rem; font-size: 0.55rem;
color: #c8b99a; color: var(--color-parchment-border);
line-height: 1; line-height: 1;
} }
+2 -8
View File
@@ -1,19 +1,13 @@
.button { .button {
composes: pressable from '../styles/shared.module.css';
width: 100%; width: 100%;
padding: 1rem; padding: 1rem;
background: #a8d5e8; background: var(--color-blue);
border: none; border: none;
border-radius: 1rem; border-radius: 1rem;
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
transition: transform 80ms ease, box-shadow 80ms ease;
}
.button:active:not(:disabled) {
transform: translateY(2px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
} }
.button:disabled { .button:disabled {
+7 -1
View File
@@ -1,8 +1,14 @@
:root {
--color-blue: #a8d5e8;
--color-parchment: #f5f0e4;
--color-parchment-border: #c8b99a;
}
*, *::before, *::after { box-sizing: border-box; } *, *::before, *::after { box-sizing: border-box; }
button { min-height: 2.75rem; font-size: 1rem; font-family: inherit; cursor: pointer; } button { min-height: 2.75rem; font-size: 1rem; font-family: inherit; cursor: pointer; }
html, body, #root { margin: 0; height: 100%; } html, body, #root { margin: 0; height: 100%; }
body { body {
font-family: sans-serif; font-family: sans-serif;
background: linear-gradient(to bottom, #a8d5e8, #4a9e6a); background: linear-gradient(to bottom, var(--color-blue), #4a9e6a);
min-height: 100vh; min-height: 100vh;
} }
+13 -28
View File
@@ -10,7 +10,7 @@
padding: 0.85rem 1rem; padding: 0.85rem 1rem;
font-size: 1.1rem; font-size: 1.1rem;
font-family: inherit; font-family: inherit;
border: 1.5px solid #c8b99a; border: 1.5px solid var(--color-parchment-border);
border-radius: 0.75rem; border-radius: 0.75rem;
background: #fdfaf4; background: #fdfaf4;
} }
@@ -20,27 +20,25 @@
background: #fff5f5; background: #fff5f5;
} }
.removeBtn { .circleBtn {
flex-shrink: 0; composes: pressable from '../styles/shared.module.css';
width: 2.5rem;
height: 2.5rem;
border-radius: 50%; border-radius: 50%;
border: none; border: none;
background: #d94f4f;
color: white;
font-size: 1.25rem;
line-height: 1; line-height: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
transition: transform 80ms ease, box-shadow 80ms ease;
} }
.removeBtn:active { .removeBtn {
transform: translateY(2px); composes: circleBtn;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); flex-shrink: 0;
width: 2.5rem;
height: 2.5rem;
background: #d94f4f;
color: white;
font-size: 1.25rem;
} }
.addRow { .addRow {
@@ -50,22 +48,9 @@
} }
.addBtn { .addBtn {
composes: circleBtn;
width: 3rem; width: 3rem;
height: 3rem; height: 3rem;
border-radius: 50%; background: var(--color-blue);
border: none;
background: #a8d5e8;
font-size: 1.75rem; font-size: 1.75rem;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
transition: transform 80ms ease, box-shadow 80ms ease;
}
.addBtn:active {
transform: translateY(2px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
} }
+1 -7
View File
@@ -1,14 +1,8 @@
.frame { .frame {
position: fixed; composes: card from '../styles/shared.module.css';
inset: 1rem;
border: 2px solid black;
border-radius: 2rem;
background: transparent; background: transparent;
display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden;
} }
.title { .title {
+19
View File
@@ -0,0 +1,19 @@
.card {
position: fixed;
inset: 1rem;
border: 2px solid black;
border-radius: 2rem;
display: flex;
flex-direction: column;
overflow: hidden;
}
.pressable {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
transition: transform 80ms ease, box-shadow 80ms ease;
}
.pressable:active:not(:disabled) {
transform: translateY(2px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}