Skip to content

Commit db3595c

Browse files
davidortinauCopilot
andcommitted
fix: vocabulary matching uniform box layout, no scroll #131
- Replace inline status text with compact icon bar + toast notifications - Tile grid uses CSS Grid with equal fr rows/columns for uniform sizing - Tiles use clamp() font-size to scale text within fixed cells - overflow: hidden on activity-content prevents page scrolling - Mobile: 2-col 8-row grid; landscape: 4-col 4-row tight layout - All game feedback (match, miss, instructions) via ToastService Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3b0e8db commit db3595c

File tree

2 files changed

+71
-27
lines changed

2 files changed

+71
-27
lines changed

src/SentenceStudio.UI/Pages/VocabMatching.razor

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,20 @@ else if (isBusy)
4545
}
4646
else if (tiles.Any())
4747
{
48-
@* Status bar *@
49-
<p class="ss-body1 text-center mb-3">
50-
Matched: @matchedPairs / @totalPairs &nbsp;|&nbsp; Misses: @incorrectGuesses
51-
@if (!string.IsNullOrEmpty(gameMessage))
52-
{
53-
<br /><small class="text-secondary-ss">@gameMessage</small>
54-
}
55-
</p>
48+
@* Compact status — single line, no wrapping *@
49+
<div class="matching-status-bar">
50+
<span><i class="bi bi-check-circle-fill text-success"></i> @matchedPairs/@totalPairs</span>
51+
<span><i class="bi bi-x-circle-fill text-danger"></i> @incorrectGuesses</span>
52+
</div>
5653

57-
@* Tile grid — fills remaining height *@
54+
@* Tile grid — uniform boxes, fills remaining height, no scroll *@
5855
<div class="matching-tile-grid flex-grow-1">
5956
@foreach (var tile in tiles)
6057
{
6158
var t = tile;
6259
if (!t.IsVisible && !t.IsMatched)
6360
{
64-
<div></div>
61+
<div class="matching-tile-placeholder"></div>
6562
continue;
6663
}
6764

@@ -70,10 +67,10 @@ else if (tiles.Any())
7067
var opacity = t.IsMatched ? "0.3" : "1.0";
7168
var cursor = t.IsMatched ? "default" : "pointer";
7269

73-
<div class="card card-ss clickable text-center @bgClass"
74-
style="cursor: @cursor; opacity: @opacity; border-width: 2px !important; display: flex; align-items: center; justify-content: center;"
70+
<div class="matching-tile @bgClass"
71+
style="cursor: @cursor; opacity: @opacity;"
7572
@onclick="() => OnTileTapped(t)">
76-
<span class="ss-title3 @textClass">@t.Text</span>
73+
<span class="@textClass">@t.Text</span>
7774
</div>
7875
}
7976
</div>
@@ -109,7 +106,6 @@ else
109106
private bool isBusy;
110107
private bool isGameComplete;
111108
private bool hideNativeWordsMode = true;
112-
private string gameMessage = "";
113109
private int matchedPairs;
114110
private int totalPairs;
115111
private int incorrectGuesses;
@@ -129,7 +125,6 @@ else
129125
private async Task LoadVocabulary()
130126
{
131127
isBusy = true;
132-
gameMessage = "";
133128
try
134129
{
135130
var resourceIds = ParseResourceIds();
@@ -150,7 +145,7 @@ else
150145

151146
if (!allWords.Any())
152147
{
153-
gameMessage = "No vocabulary available.";
148+
Toast.ShowWarning("No vocabulary available.");
154149
isBusy = false;
155150
return;
156151
}
@@ -215,7 +210,8 @@ else
215210
incorrectGuesses = 0;
216211
isGameComplete = false;
217212
selectedTiles.Clear();
218-
gameMessage = hideNativeWordsMode ? "Tap a target word to reveal native words" : "Match the pairs!";
213+
var hint = hideNativeWordsMode ? "Tap a target word to reveal native words" : "Match the pairs!";
214+
Toast.ShowInfo(hint, 2000);
219215
}
220216
catch (Exception ex)
221217
{
@@ -258,10 +254,9 @@ else
258254
}
259255

260256
if (selectedTiles.Count == 1)
261-
gameMessage = "Select another tile";
257+
Toast.ShowInfo("Now pick the match", 1500);
262258
else if (selectedTiles.Count == 2)
263259
{
264-
gameMessage = "Checking...";
265260
_ = CheckMatchWithDelay();
266261
}
267262
}
@@ -275,11 +270,9 @@ else
275270
{
276271
foreach (var t in tiles.Where(t => t.Language == "native" && !t.IsMatched))
277272
t.IsVisible = false;
278-
gameMessage = "Tap a target word to reveal native words";
279273
}
280274
else
281275
{
282-
gameMessage = "Match the pairs!";
283276
}
284277
}
285278

@@ -348,7 +341,7 @@ else
348341
tile2.IsSelected = false;
349342
selectedTiles.Clear();
350343
matchedPairs++;
351-
gameMessage = "Great match!";
344+
Toast.ShowSuccess("Great match!", 1500);
352345

353346
if (hideNativeWordsMode)
354347
{
@@ -359,7 +352,6 @@ else
359352
if (matchedPairs >= totalPairs)
360353
{
361354
isGameComplete = true;
362-
gameMessage = "";
363355
foreach (var t in tiles) t.IsVisible = true;
364356
Toast.ShowSuccess("All pairs matched!");
365357
}
@@ -370,7 +362,7 @@ else
370362
tile2.IsSelected = false;
371363
selectedTiles.Clear();
372364
incorrectGuesses++;
373-
gameMessage = "Not a match. Try again!";
365+
Toast.ShowWarning("Not a match — try again!", 1500);
374366

375367
if (hideNativeWordsMode)
376368
{
@@ -388,7 +380,6 @@ else
388380
totalPairs = 0;
389381
incorrectGuesses = 0;
390382
isGameComplete = false;
391-
gameMessage = "";
392383
await LoadVocabulary();
393384
}
394385

src/SentenceStudio.UI/wwwroot/css/app.css

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,17 +1417,70 @@ h3:focus,
14171417
}
14181418

14191419
/* ─── Vocabulary Matching tile grid ─── */
1420+
/* ─── Vocabulary Matching — uniform tiles, no scroll ─── */
1421+
1422+
.matching-status-bar {
1423+
display: flex;
1424+
justify-content: center;
1425+
gap: 1.25rem;
1426+
padding: 0.25rem 0;
1427+
font-size: 0.875rem;
1428+
flex-shrink: 0;
1429+
}
1430+
14201431
.matching-tile-grid {
14211432
display: grid;
14221433
grid-template-columns: repeat(4, 1fr);
14231434
grid-template-rows: repeat(4, 1fr);
1424-
gap: 0.75rem;
1425-
align-content: stretch;
1435+
gap: 0.5rem;
14261436
min-height: 0;
1437+
overflow: hidden;
14271438
}
1439+
1440+
.matching-tile,
1441+
.matching-tile-placeholder {
1442+
display: flex;
1443+
align-items: center;
1444+
justify-content: center;
1445+
border: 2px solid var(--bs-border-color);
1446+
border-radius: var(--bs-border-radius);
1447+
background: var(--bs-body-bg);
1448+
padding: 0.25rem;
1449+
overflow: hidden;
1450+
text-align: center;
1451+
font-size: clamp(0.7rem, 2vw, 1rem);
1452+
line-height: 1.2;
1453+
word-break: break-word;
1454+
min-width: 0;
1455+
min-height: 0;
1456+
}
1457+
1458+
.matching-tile-placeholder {
1459+
border-color: transparent;
1460+
background: transparent;
1461+
}
1462+
14281463
@media (max-width: 768px) {
14291464
.matching-tile-grid {
14301465
grid-template-columns: repeat(2, 1fr);
1466+
grid-template-rows: repeat(8, 1fr);
1467+
gap: 0.375rem;
1468+
}
1469+
1470+
.matching-tile {
1471+
font-size: clamp(0.75rem, 3.5vw, 1rem);
1472+
}
1473+
}
1474+
1475+
@media (orientation: landscape) and (max-height: 500px) {
1476+
.matching-tile-grid {
1477+
grid-template-columns: repeat(4, 1fr);
1478+
grid-template-rows: repeat(4, 1fr);
1479+
gap: 0.25rem;
1480+
}
1481+
1482+
.matching-tile {
1483+
font-size: clamp(0.65rem, 2vw, 0.85rem);
14311484
}
14321485
}
14331486

0 commit comments

Comments
 (0)