feat(css): site público passa de Bootstrap 4.3.1 (EOL) para 5.3.3

Auditoria: "Site público em Bootstrap 4.3.1 (EOL); backoffice em
5.3.3" — o backoffice já usa a 5.3.3 vendorizada (assets/vendor/bootstrap5/),
o site público passa a usar a mesma cópia em vez da 4.3.1.

Levantamento antes de mexer: a folha de estilo própria (style.css)
quase não usa classes utilitárias cruas do Bootstrap (grep confirma
zero ml-/mr-/pl-/pr-/float-left/float-right/custom-select/
custom-control/badge-/sr-only/font-weight-/text-left/text-right —
tudo classes próprias como .site-btn). O que precisava de mudar:

- CSS/JS: assets/vendor/bootstrap/ -> assets/vendor/bootstrap5/
  (bundle, inclui Popper).
- Os 7 atributos data-toggle/data-target/data-dismiss (BS4) ->
  data-bs-toggle/data-bs-target/data-bs-dismiss (BS5): toggle da nav
  mobile, os dois modais (aula experimental, inscrição), o botão de
  fechar do alerta. O seletor em main.js que lê data-target também
  mudou para não parar de encontrar os botões.
- .close -> .btn-close (BS5 renomeou o componente; markup sem o
  × interno, que o BS5 desenha via CSS).
- .form-row -> .row (BS4 tinha form-row com gutters menores; BS5 não
  tem, style.css não tinha overrides para form-row, confirmado).
- O apanhado maior: dois <script> inline chamavam
  jQuery('#trialModal').modal('show') ao estilo de plugin jQuery do
  BS4 (para reabrir o modal depois de um erro de submissão) — o BS5
  não regista plugins jQuery nenhuns, isto ficava morto em silêncio.
  Trocado por new bootstrap.Modal(...).show(), a API JS nativa do
  BS5. grep confirma zero outras chamadas .modal()/.collapse()/etc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
andreandClaude Sonnet 5 committed 2026-09-25 21:47:15 +01:00
1 parent b837def5a0
commit 1e2068bea2
5 files changed
+13 -13

No files matched your search

+1 -1
View File
@@ -691,7 +691,7 @@ document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('DOMContentLoaded', function () {
var select = document.querySelector('#joinModal select[name="membership_id"]');
if (!select) return;
document.querySelectorAll('[data-target="#joinModal"][data-membership]').forEach(function (btn) {
document.querySelectorAll('[data-bs-target="#joinModal"][data-membership]').forEach(function (btn) {
btn.addEventListener('click', function () { select.value = btn.getAttribute('data-membership'); });
});
});
+1 -1
View File
@@ -37,7 +37,7 @@
<!-- Scripts: jQuery first (Bootstrap navbar/alerts depend on it), then plugins -->
<?= script_tag('assets/vendor/jquery/jquery-3.7.1.min.js') ?>
<?= script_tag('assets/vendor/bootstrap/js/bootstrap.min.js') ?>
<?= script_tag('assets/vendor/bootstrap5/js/bootstrap.bundle.min.js') ?>
<?= script_tag('site/assets/js/main.js') ?>
</body>
</html>
+1 -1
View File
@@ -58,7 +58,7 @@ $canonical = $GLOBALS['_canonical_override'] ?? match ($page) {
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700,900&display=swap" rel="stylesheet">
<!-- Vendor: Bootstrap 4.3.1 -->
<?= link_tag('assets/vendor/bootstrap/css/bootstrap.min.css') ?>
<?= link_tag('assets/vendor/bootstrap5/css/bootstrap.min.css') ?>
<!-- Vendor: Font Awesome 5 -->
<?= link_tag('site/assets/fonts/fontawesome/css/all.css') ?>
+1 -1
View File
@@ -14,7 +14,7 @@ $logo_dims = svg_img_dims_attr(dirname(__DIR__, 2) . '/' . $logo_rel);
<a class="navbar-brand" href="<?= htmlspecialchars(gym_url(current_gym())) ?>">
<img loading="eager" decoding="async" alt="Logo FitMe3070" src="<?= $logo ?>" <?= $logo_dims ?> style="width: 150px;">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mx-auto ">
<?php foreach($navbar as $key => $n):
+9 -9
View File
@@ -73,7 +73,7 @@ $hero_img = $gym['hero_image'] !== '' ? $gym['hero_image'] : 'site/assets/img
<h1><?= htmlspecialchars($gym['hero_title'] !== '' ? $gym['hero_title'] : $gym['name']) ?><?php if ($gym['hero_subtitle'] !== ''): ?><br><span><?= htmlspecialchars($gym['hero_subtitle']) ?></span><?php endif; ?></h1>
<div class="hero-cta-row">
<a href="?assunto=insc#contactos" class="site-btn">Inscreve-te Já!</a>
<button type="button" class="site-btn sb-ghost" data-toggle="modal" data-target="#trialModal">Marca uma Aula Experimental</button>
<button type="button" class="site-btn sb-ghost" data-bs-toggle="modal" data-bs-target="#trialModal">Marca uma Aula Experimental</button>
</div>
</div>
</div>
@@ -496,7 +496,7 @@ if ($memberships):
<?php foreach ($features as $f): ?><li><i class="fas fa-check" aria-hidden="true"></i><?= htmlspecialchars($f) ?></li><?php endforeach; ?>
</ul>
<?php endif; ?>
<button type="button" class="site-btn pc-cta" data-toggle="modal" data-target="#joinModal"
<button type="button" class="site-btn pc-cta" data-bs-toggle="modal" data-bs-target="#joinModal"
data-membership="<?= (int) $m['id'] ?>"><?= htmlspecialchars($m['cta_label']) ?></button>
</article>
<?php endforeach; ?>
@@ -670,7 +670,7 @@ $session = session();
<?php if ($session->getFlashdata('email') !== null): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= $session->getFlashdata('email') ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<label class="consent-check">
@@ -752,12 +752,12 @@ if ($on('personal_trainer')):
<span class="section-eyebrow">FitMe 3070</span>
<h3 id="trialModalLabel">Marca a tua aula experimental</h3>
<p>Treina connosco sem compromisso. Deixa os teus dados e entramos em contacto para confirmar o dia e a hora.</p>
<button type="button" class="trial-close" data-dismiss="modal" aria-label="Fechar">&times;</button>
<button type="button" class="trial-close" data-bs-dismiss="modal" aria-label="Fechar">&times;</button>
</div>
<form class="trial-form" method="post" action="<?= htmlspecialchars(gym_url($gym, 'agendar')) ?>">
<?= csrf_field() ?>
<?= public_form_trap('trial') /* honeypot + carimbo + prova de JS */ ?>
<div class="form-row">
<div class="row">
<div class="col-12 col-md-6 mb-3">
<input type="text" name="nome" required placeholder="Nome *" aria-label="Nome">
</div>
@@ -796,7 +796,7 @@ if ($on('personal_trainer')):
</div>
</div>
<?php if (($_GET['agendar'] ?? '') === 'erro'): ?>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.jQuery){jQuery('#trialModal').modal('show');}});</script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.bootstrap){new bootstrap.Modal(document.getElementById('trialModal')).show();}});</script>
<?php endif; ?>
<?php
@@ -812,12 +812,12 @@ if ($memberships):
<span class="section-eyebrow"><?= htmlspecialchars($info['name'] !== '' ? $info['name'] : APP_NAME) ?></span>
<h3 id="joinModalLabel">Inscreve-te</h3>
<p>Deixa os teus contactos e entramos em contacto para concluir a inscrição.</p>
<button type="button" class="trial-close" data-dismiss="modal" aria-label="Fechar">&times;</button>
<button type="button" class="trial-close" data-bs-dismiss="modal" aria-label="Fechar">&times;</button>
</div>
<form class="trial-form" method="post" action="<?= htmlspecialchars(gym_url($gym, 'inscricao')) ?>">
<?= csrf_field() ?>
<?= public_form_trap('inscricao') /* honeypot + carimbo + prova de JS */ ?>
<div class="form-row">
<div class="row">
<div class="col-12 mb-3">
<select name="membership_id" aria-label="Mensalidade" required>
<?php foreach ($memberships as $m): ?>
@@ -852,7 +852,7 @@ if ($memberships):
</div>
</div>
<?php if (($_GET['inscricao'] ?? '') === 'erro'): ?>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.jQuery){jQuery('#joinModal').modal('show');}});</script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.bootstrap){new bootstrap.Modal(document.getElementById('joinModal')).show();}});</script>
<?php endif; ?>
<?php endif; ?>