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() {
const [page, setPage] = useState<Page>(() =>
loadGame() ? 'scoresheet' : 'title'
)
const [game, setGame] = useState<GameState | null>(() => loadGame())
const [game, setGame] = useState<GameState | null>(loadGame)
const [page, setPage] = useState<Page>(() => (game ? 'scoresheet' : 'title'))
function handleNewGame() {
clearGame()
+5 -11
View File
@@ -1,12 +1,6 @@
.frame {
position: fixed;
inset: 1rem;
border: 2px solid black;
border-radius: 2rem;
background: #f5f0e4;
display: flex;
flex-direction: column;
overflow: hidden;
composes: card from '../styles/shared.module.css';
background: var(--color-parchment);
}
.header {
@@ -23,7 +17,7 @@
.rule {
position: relative;
border: none;
border-top: 1px solid #c8b99a;
border-top: 1px solid var(--color-parchment-border);
margin: 0.75rem 0 0;
}
@@ -33,10 +27,10 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #f5f0e4;
background: var(--color-parchment);
padding: 0 0.35em;
font-size: 0.55rem;
color: #c8b99a;
color: var(--color-parchment-border);
line-height: 1;
}
+2 -8
View File
@@ -1,19 +1,13 @@
.button {
composes: pressable from '../styles/shared.module.css';
width: 100%;
padding: 1rem;
background: #a8d5e8;
background: var(--color-blue);
border: none;
border-radius: 1rem;
font-size: 1.25rem;
font-weight: 600;
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 {
+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; }
button { min-height: 2.75rem; font-size: 1rem; font-family: inherit; cursor: pointer; }
html, body, #root { margin: 0; height: 100%; }
body {
font-family: sans-serif;
background: linear-gradient(to bottom, #a8d5e8, #4a9e6a);
background: linear-gradient(to bottom, var(--color-blue), #4a9e6a);
min-height: 100vh;
}
+13 -28
View File
@@ -10,7 +10,7 @@
padding: 0.85rem 1rem;
font-size: 1.1rem;
font-family: inherit;
border: 1.5px solid #c8b99a;
border: 1.5px solid var(--color-parchment-border);
border-radius: 0.75rem;
background: #fdfaf4;
}
@@ -20,27 +20,25 @@
background: #fff5f5;
}
.removeBtn {
flex-shrink: 0;
width: 2.5rem;
height: 2.5rem;
.circleBtn {
composes: pressable from '../styles/shared.module.css';
border-radius: 50%;
border: none;
background: #d94f4f;
color: white;
font-size: 1.25rem;
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;
}
.removeBtn:active {
transform: translateY(2px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
.removeBtn {
composes: circleBtn;
flex-shrink: 0;
width: 2.5rem;
height: 2.5rem;
background: #d94f4f;
color: white;
font-size: 1.25rem;
}
.addRow {
@@ -50,22 +48,9 @@
}
.addBtn {
composes: circleBtn;
width: 3rem;
height: 3rem;
border-radius: 50%;
border: none;
background: #a8d5e8;
background: var(--color-blue);
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 {
position: fixed;
inset: 1rem;
border: 2px solid black;
border-radius: 2rem;
composes: card from '../styles/shared.module.css';
background: transparent;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
}
.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);
}