Ho fatto un po' di ricerche e provato diverse soluzioni, applicabili e non, ognuna con i suoi pro e contro.
Prima di indicarti alcune possibili soluzioni faccio una piccola premessa perché mi preme chiarire alcuni punti.
Per creare le ancore stai usando una tecnica obsoleta, con una struttura del genere:
codice:
<h2>
<a name="nome-ancora"></a>
Titolo
</h2>
In HTML5 l'uso dell'attributo "name", applicato agli elementi <a> per creare delle ancore interne, è obsoleto, anche se i vari browser possono riuscire a digerirlo; dovresti invece usare l'id (col relativo nome dell'ancora) applicandolo direttamente agli <h2> in questo modo:
codice:
<h2 id="nome-ancora">Titolo</h2>
Altra cosa, ho provato su Edge e IE11, e vedo che il sistema del target, da te usato, non funziona. Non ho approfondito ma è giusto per fartelo presente. Fai tu eventuali verifiche.
---
Qui le varie possibili strade:
A) Uso di scroll-padding-top. Togli il trick :target:before e applica giusto questa regola:
codice:
html, body {
scroll-padding-top: 86px;
}
PRO: questa è la soluzione più semplice e pulita che ho trovato e provato, supportata dalle versioni più recenti dei diversi browser.
CONTRO: non è supportata da qualche browser non proprio recente. Su caniuse puoi vedere l'attuale supporto dei vari browser e valutare tu se usarla.
B) Usando il trick :target:before (che già hai) applica overflow:hidden al div che contiene l'elemento <h2>. Nella tua specifica situazione, ogni <h2> è contenuto dentro un <div class="col-12"> per cui potresti fare una cosa del genere:
codice:
.col-12 {
overflow: hidden;
}
Dal momento che il div non subisce l'alterazione della sua altezza, anche se l'altezza dell'elemento <h2> viene maggiorata, quel overflow:hidden non fa altro che "confinare" il contenuto stesso all'interno dell'area del div, che resta sostanzialmente invariata.
PRO: di facile applicazione nella tua situazione attuale, dato che si appoggia al corrente trick.
CONTRO: si comporta in modo differente a seconda del browser. Funziona perfettamente su FF66, su cui l'ho testato, mentre su CH vedo che la pagina viene portata al target ma senza considerare quell'offset relativo al contenuto nascosto, creato col :before (può essere che allo stesso modo non funzioni su versioni più recenti di FF, attualmente la 75). Su ED/IE non è possibile verificare dato che il trick :target:before non funziona.
Boccerei comunque questa soluzione dato che fallisce su recenti browser, come CH81 su cui sto testando.
C) Uso di background:linear-gradient e pointer-events:none per <h2>.
Sostanzialmente creo per <h2> uno sfondo a due fasce, di cui quella di sopra è trasparente, quindi lo rendo "immune" agli eventi mouse.
In questo caso va aggiustato l'HTML come indicato nella premessa, cioè vanno rimossi tutti quegli <a name="..."> (che comunque risultano obsoleti) e va impostato un relativo id su ciascun <h2>. Questa mi pare comunque una cosa che dovresti fare per avere un codice perfettamente valido, a prescindere dalla risoluzione del problema in oggetto.
Per farlo in modo veloce e automatico, può essere utile usare un editor con la funzione trova/sostituisci che supporti le espressioni regolari.
Io l'ho fatto con Notepad++ impostando la sostituzione in questo modo:
codice:
Trova: <(.+?)>([\s]*?)<a name="(.+?)"></a>
Sostituisci con: <$1 id="$3">$2
A questo punto dovrai chiaramente togliere la regola :target:before e quindi sostituire la regola definita per h2, con questa:
codice:
h2 {
font-size: 20px;
font-weight: bold;
text-align: center;
color: #ffffff;
padding-top: 86px; /* valore per l'offset */
margin-top: -76px; /* riposizionamento rispetto all'offset. I 10px che mancano sono per il normale margin */
pointer-events: none; /* lo rendo inattivo agli eventi mouse per risolvere il problema della sovrapposizione */
background: -webkit-linear-gradient(transparent 86px, #f75740 86px);
background: -o-linear-gradient(transparent 86px, #f75740 86px);
background: linear-gradient(transparent 86px, #f75740 86px); /* applico la trasparenza per l'altezza dell'offset */
}
I commenti mi sembrano abbastanza chiari, ad ogni modo riassumo ciò che avviene:
- in sostanza il padding serve a estendere l'altezza di h2 in riferimento al valore di offset da considerare;
- il margin negativo "azzera" lo spostamento dovuto a questo sovradimensionamento, così come più o meno avviene per quel trick :target:before;
- il pointer-events:none disabilita gli eventi del mouse su questo elemento, in modo che anche se questo viene a trovarsi sopra altri elementi, non vada ad impedire l'interazione del mouse con loro;
- il linear-gradient impostato per il background crea lo sfondo con due fasce di cui quella sopra, alta quanto il valore dell'offset, è trasparente, mentre quella sotto è del colore impostato normalmente per lo sfondo di h2.
PRO: soluzione abbastanza pulita e funziona perfettamente sui vari browser, recenti e un po' meno recenti (come IE11). Risolve peraltro il mancato funzionamento su ED/IE che attualmente hai con quel trick. E cosa importante, ti costringe a sistemare le ancore interne così da avere un HTML perfettamente valido.
CONTRO: va impiegato un po' di tempo per aggiustare l'HTML ma penso ne valga la pena. Eventuali elementi cliccabili (ad esempio link) contenuti in <h2>, restano "insensibili" agli eventi mouse dal momento che è applicato pointer-events:none; ad ogni modo, se si vuole renderli nuovamente sensibili, è sufficiente applicare, a questi, pointer-events:auto, in modo che tale proprietà sovrascriva quella del genitore h2 (il quale manterrà comunque la sua giusta "insensibilità").
D) Usare una soluzione JavaScript/jQuery. Non ne ho da proporti ma sono facilmente reperibili con qualche ricerca.