Compare commits

..

2 Commits

Author SHA1 Message Date
Marijn 20c97d9cb5 Improve quiz page layouts: WIDM-style answers and responsive centering
- Add green square answer buttons styled after the TV show
- Two-column answer grid for 6+ answers, single column on mobile
- fit-content centering for question pages so block matches question width
- Narrow fixed-width centering for form pages (enter name, select season)
2026-07-01 22:25:43 +02:00
Marijn e5198507ae Fix quiz page vertical centering regression
The CSS cleanup broke vertical centering: flex on body causes main to
stretch full-width; place-items:center on a grid body only centers
items within their auto-sized track (not the track within body).

Fix: move background/color to html (full-viewport grid that centers
body), give body height:100% + display:grid + align-content:center
(centers the content track within full-height body) + justify-self:center
(shrink-wraps body width). Matches production behavior exactly.
2026-07-01 21:35:45 +02:00
5 changed files with 123 additions and 40 deletions
+77 -6
View File
@@ -1,17 +1,88 @@
html {
height: 100%;
}
body {
min-height: 100%;
background-image: url("../img/background.png");
background-position: center;
background-repeat: no-repeat;
background-color: black;
color: white;
display: grid;
align-items: center;
}
body {
height: 100%;
background-image: url("../img/background.png");
background-position: center;
background-repeat: no-repeat;
background-color: black;
display: grid;
align-content: center;
}
.quiz-form-narrow {
max-width: 320px;
width: 100%;
margin: 0 auto;
}
.quiz-content {
width: fit-content;
max-width: min(760px, 100%);
margin: 0 auto;
}
.quiz-answers-grid {
display: grid;
grid-template-columns: repeat(2, auto);
column-gap: 2rem;
@media (max-width: 767px) {
grid-template-columns: 1fr;
}
}
.answer-btn {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.8rem;
background: transparent;
border: none;
color: white;
font-size: 1.1rem;
padding: 0.4rem 0;
cursor: pointer;
text-align: left;
line-height: 1.3;
&::before {
content: '';
display: block;
flex-shrink: 0;
width: 2rem;
height: 2rem;
background: radial-gradient(ellipse at 35% 30%, #6abf4b, #2d7a1f);
border-radius: 4px;
box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.25), 0 2px 6px rgba(0, 0, 0, 0.6);
}
&:hover,
&:focus {
color: #7ed45a;
outline: none;
&::before {
background: radial-gradient(ellipse at 35% 30%, #86d45f, #3d9c2a);
}
}
}
input.btn-check:checked + label.answer-btn {
color: #7ed45a;
&::before {
background: radial-gradient(ellipse at 35% 30%, #86d45f, #3d9c2a);
box-shadow: 0 0 10px rgba(106, 191, 75, 0.7), inset 0 1px 2px rgba(255, 255, 255, 0.3);
}
}
.elimination-screen {
+3 -1
View File
@@ -1,4 +1,6 @@
{% extends 'quiz/base.html.twig' %}
{% block body %}
{{ form(form) }}
<div class="quiz-form-narrow">
{{ form(form) }}
</div>
{% endblock body %}
+36 -29
View File
@@ -1,36 +1,43 @@
{% extends 'quiz/base.html.twig' %}
{% block body %}
<h4>
{% if season.settings.showNumbers %}
({{ question.ordering }}/{{ question.quiz.questions.count }})
{% endif %}{{ question.question }}<br/>
</h4>
<form method="post">
<input type="hidden" name="token" value="{{ csrf_token('question') }}">
{% if season.settings.confirmAnswers == false %}
{% for answer in question.answers %}
<div class="quiz-content">
<h4>
{% if season.settings.showNumbers %}
({{ question.ordering }}/{{ question.quiz.questions.count }})
{% endif %}{{ question.question }}<br/>
</h4>
<form method="post">
<input type="hidden" name="token" value="{{ csrf_token('question') }}">
{% set twoCol = question.answers|length >= 6 %}
{% if season.settings.confirmAnswers == false %}
<div class="{{ twoCol ? 'quiz-answers-grid' : '' }}">
{% for answer in question.answers %}
<div class="py-2">
<button class="answer-btn"
type="submit"
name="answer"
value="{{ answer.id }}">{{ answer.text }}</button>
</div>
{% endfor %}
</div>
{% else %}
<div class="{{ twoCol ? 'quiz-answers-grid' : '' }}">
{% for answer in question.answers %}
<div class="py-1">
<input required="required" type="radio" class="btn-check" name="answer"
id="answer-{{ loop.index0 }}" autocomplete="off"
value="{{ answer.id }}">
<label class="answer-btn" for="answer-{{ loop.index0 }}">{{ answer.text }}</label>
</div>
{% endfor %}
</div>
<div class="py-2">
<button class="btn btn-outline-success"
<button class="btn btn-success"
type="submit"
name="answer"
value="{{ answer.id }}">{{ answer.text }}</button>
>{{ 'Next'|trans }}</button>
</div>
{% endfor %}
{% else %}
{% for answer in question.answers %}
<div class="py-1">
<input required="required" type="radio" class="btn-check" name="answer"
id="answer-{{ loop.index0 }}" autocomplete="off"
value="{{ answer.id }}">
<label class="btn btn-outline-secondary" for="answer-{{ loop.index0 }}">{{ answer.text }}</label>
</div>
{% endfor %}
<div class="py-2">
<button class="btn btn-success"
type="submit"
>{{ 'Next'|trans }}</button>
</div>
{% endif %}
</form>
{% endif %}
</form>
</div>
{% endblock body %}
+4 -2
View File
@@ -1,5 +1,7 @@
{% extends 'quiz/base.html.twig' %}
{% block body %}
{{ form(form) }}
<a href="{{ path('tvdt_backoffice_index') }}" class="btn btn-outline-secondary">{{ 'Manage Quiz'|trans }}</a>
<div class="quiz-form-narrow">
{{ form(form) }}
<a href="{{ path('tvdt_backoffice_index') }}" class="btn btn-outline-secondary">{{ 'Manage Quiz'|trans }}</a>
</div>
{% endblock body %}
+3 -2
View File
@@ -127,6 +127,7 @@ final class QuizSpreadsheetServiceTest extends TestCase
$question = new Question();
$question->question = 'Unanswered question';
$question->ordering = 1;
$quiz->addQuestion($question);
$path = $this->captureXlsx($this->subject->quizToXlsx($quiz));
@@ -134,8 +135,8 @@ final class QuizSpreadsheetServiceTest extends TestCase
try {
$this->subject->xlsxToQuiz(new Quiz(), new File($path));
$this->fail('Expected SpreadsheetDataException to be thrown');
} catch (SpreadsheetDataException $e) {
$this->assertNotEmpty($e->errors);
} catch (SpreadsheetDataException $spreadsheetDataException) {
$this->assertNotEmpty($spreadsheetDataException->errors);
}
}