CSS: mettere in grassetto un testo senza modificare la dimensione del suo contenitore


119

Ho un menu di navigazione orizzontale, che è fondamentalmente solo un <ul>con gli elementi affiancati. Non definisco la larghezza, ma uso semplicemente il padding, perché vorrei che le larghezze fossero definite dalla larghezza della voce di menu. Metto in grassetto l'elemento attualmente selezionato.

Il problema è che in grassetto la parola diventa leggermente più larga, il che fa sì che il resto degli elementi si sposti leggermente a sinistra oa destra. C'è un modo intelligente per evitare che ciò accada? Qualcosa sulla falsariga di dire all'imbottitura di ignorare la larghezza extra causata dal grassetto? Il mio primo pensiero è stato quello di sottrarre semplicemente alcuni pixel dal riempimento dell'elemento "attivo", ma questa quantità varia.

Se possibile, vorrei evitare di impostare una larghezza statica su ciascuna voce e quindi centrarla in contrasto con la soluzione di riempimento che ho attualmente, al fine di rendere semplici le modifiche future alle voci.


perché il cambio di taglia è un problema? In qualche modo sta incasinando il layout?
Chaulky

Penso che le domande sulla programmazione web siano meglio poste su Stack Overflow. Forse un mod lo migrerà lì.
Ciaran

2
Chaulky: perché ci sono poche cose che odio di più, dal punto di vista del layout, rispetto a quando i pulsanti su cui dovresti provare a fare clic per saltare
Mala

doejo.com/blog/… è una soluzione che ho trovato che fa esattamente quello che John e Blowski stavano parlando di jscript.
thebulfrog

Risposte:


152

Ho avuto lo stesso problema, ma ho ottenuto un effetto simile con un piccolo compromesso, ho usato invece l'ombra del testo.

li:hover {text-shadow:0px 0px 1px black;}

Ecco un esempio funzionante:

body {
  font-family: segoe ui;
}

ul li {
  display: inline-block;
  border-left: 1px solid silver;
  padding: 5px
}

.textshadow :hover {
  text-shadow: 0px 0px 1px black;
}

.textshadow-alt :hover {
  text-shadow: 1px 0px 0px black;
}

.bold :hover {
  font-weight: bold;
}
<ul class="textshadow">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>text-shadow: 0px 0px 1px black;</code></li>
</ul>

<ul class="textshadow-alt">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>text-shadow: 1px 0px 0px black;</code></li>
</ul>

<ul class="bold">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>font-weight: bold;</code></li>
</ul>

jsfiddle esempio


51
Adoro questa soluzione, anche se consiglio di capovolgerla su 1px 0px 0px. Sembra un po 'più audace.
M. Herold

Avevo bisogno di una soluzione solo css e questa è la risposta.
JCasso

sei un vero genio! la semplicità lo rende stupendo!
lufi

1
Solo l'aggiunta ha li:hover {text-shadow: 1px 0 0 black;}prodotto testo con una spaziatura insufficiente tra le lettere. Per migliorare ancora una volta, abbiamo anche impostatoli {letter-spacing: 1px;}
Patrick Hammer

5
Ho usato -0.5px 0 #fff, 0.5px 0 #fff, perché potevamo vedere il piccolo spazio tra il testo stesso e l'ombra. E anche quando il testo è in grassetto, aggiunge peso a entrambi i lati.
atorscho

101

La migliore soluzione di lavoro utilizzando :: after

HTML

<li title="EXAMPLE TEXT">
  EXAMPLE TEXT
</li>

CSS

li::after {
  display: block;
  content: attr(title);
  font-weight: bold;
  height: 1px;
  color: transparent;
  overflow: hidden;
  visibility: hidden;
}

Aggiunge uno pseudo-elemento invisibile con larghezza di testo in grassetto, originato da titleattributo.

La text-shadowsoluzione sembra innaturale su Mac e non utilizza tutta la bellezza offerta dal rendering del testo su Mac .. :)

http://jsfiddle.net/85LbG/

Credito: https://stackoverflow.com/a/20249560/5061744


11
Questa è la soluzione più affidabile e pulita , riserva semplicemente spazio per il testo in grassetto nell'elemento. Nel mio caso, lo pseudo-elemento aggiuntivo ha causato il cambiamento di altezza. L'aggiunta di negativo margin-topal ::afterblocco ha risolto il problema.
Vaclav Novotny

2
Questa soluzione è fantastica! Ho usato JS per aggiungere un attributo "data-content" che contiene il testo dell'elemento in modo da evitare di modificare l'HTML.
mistykristie

1
Eccezionale. Questa dovrebbe essere la risposta migliore. Nota che se un elemento è diverso da li potresti dover usare display: block; su: dopo l'elemento genitore.
Blackbam

Ottima soluzione! Funziona bene anche con :: before, nel mio caso questo impedisce un margine aggiuntivo nella parte inferiore.
Thomas Champion

Grazie per questa soluzione. Inizialmente ho usato text-shadow ma questo non funziona in Safari poiché arrotonda i valori decimali. Dato che stavo usando 0.1px, questo è stato arrotondato a 0. La tua soluzione ha funzionato perfettamente. Ho sostituito solo l'attributo del titolo con il nome perché non volevo che i suggerimenti
venissero

32

La soluzione più portatile e visivamente piacevole sarebbe quella di usare text-shadow. Questo rivede e mostra esempi della risposta di Thorgeir usando Alexxali e le mie modifiche:

  li:hover { text-shadow: -0.06ex 0 black, 0.06ex 0 black; }

Questo mette minuscole "ombre" in nero (usa il nome / codice del colore del tuo font al posto di blackse necessario) su entrambi i lati di ogni lettera usando unità che scaleranno correttamente con il rendering del font.

avvertimento Attenzione: i px valori supportano i valori decimali, ma non avranno un aspetto così eccezionale quando la dimensione del carattere cambia (ad esempio, l'utente ridimensiona la vista con Ctrl+ +). Usa invece valori relativi.

Questa risposta utilizza frazioni di exunità poiché vengono ridimensionate con il carattere.
Nella maggior parte dei ~ impostazioni predefinite del browser * , si aspettano 1ex8pxe quindi 0.025ex0.1px.

Guarda tu stesso:

li             { color: #000; } /* set text color just in case */
.shadow0       { text-shadow: inherit; }
.shadow2       { text-shadow: -0.02ex 0 #000, 0.02ex 0 #000; }
.shadow4       { text-shadow: -0.04ex 0 #000, 0.04ex 0 #000; }
.shadow6       { text-shadow: -0.06ex 0 #000, 0.06ex 0 #000; }
.shadow8       { text-shadow: -0.08ex 0 #000, 0.08ex 0 #000; }
.bold          { font-weight: bold; }
.bolder        { font-weight: bolder; }
.after span        { display:inline-block; font-weight: bold; } /* workaholic… */
.after:hover span  { font-weight:normal; }
.after span::after { content: attr(title); font-weight: bold; display:block; 
                     height: 0; overflow: hidden; }
.ltrsp         { letter-spacing:0; font-weight:bold; } /* @cgTag */
li.ltrsp:hover { letter-spacing:0.0125ex; }
li:hover       { font-weight: normal!important; text-shadow: none!important; }
<li class="shadow0">MmmIii123 This line tests shadow0 (plain)</li>
<li class="shadow2">MmmIii123 This line tests shadow2 (0.02ex)</li>
<li class="shadow4">MmmIii123 This line tests shadow4 (0.04ex)</li>
<li class="shadow6">MmmIii123 This line tests shadow6 (0.06ex)</li>
<li class="shadow8">MmmIii123 This line tests shadow8 (0.08ex)</li>
<li class="after"><span title="MmmIii123 This line tests [title]"
                   >MmmIii123 This line tests [title]</span> (@workaholic…)</li>
<li class="ltrsp"  >MmmIii123 This line tests ltrsp (@cgTag)</li>
<li class="bold"   >MmmIii123 This line tests bold</li>
<li class="bolder" >MmmIii123 This line tests bolder</li>
<li class="shadow2 bold">MmmIii123 This line tests shadow2 (0.02ex) + bold</li>
<li class="shadow4 bold">MmmIii123 This line tests shadow4 (0.04ex) + bold</li>
<li class="shadow6 bold">MmmIii123 This line tests shadow6 (0.06ex) + bold</li>
<li class="shadow8 bold">MmmIii123 This line tests shadow8 (0.08ex) + bold</li>

Passa il mouse sulle linee renderizzate per vedere come differiscono dal testo standard.

Modifica il livello di zoom del browser ( Ctrl+ +e Ctrl+ -) per vedere come variano.

Ho aggiunto altre due soluzioni qui per il confronto: il trucco di spaziatura delle lettere di @ cgTag , che non funziona così bene poiché implica l'indovinare gli intervalli di larghezza dei caratteri, e il trucco di @ workaholic_gangster911 :: after drawing , che lascia uno spazio extra scomodo in modo che il testo in grassetto possa espandersi senza spingere gli elementi di testo adiacenti (ho messo l'attribuzione dopo il testo in grassetto così puoi vedere come non si muove).


In futuro, avremo più caratteri variabili capaci di cose come cambiare il grado del carattere tramite font-variation-settings. Il supporto del browser è in aumento (Chrome 63+, Firefox 62+) ma questo richiede ancora più dei semplici caratteri standard e pochi caratteri esistenti lo supportano.

Se incorpori un carattere variabile, sarai in grado di utilizzare CSS in questo modo:

/* Grade: Increase the typeface's relative weight/density */
@supports (font-variation-settings: 'GRAD' 150) {
  li:hover { font-variation-settings: 'GRAD' 150; }
}
/* Failover for older browsers: tiny shadows at right & left of the text
 * (replace both instances of "black" with the font color) */
@supports not (font-variation-settings: 'GRAD' 150) {
  li:hover { text-shadow: -0.06ex 0 black, 0.06ex 0 black; }
}

C'è una demo dal vivo con un cursore per giocare con vari gradi sulla Guida ai caratteri variabili di Mozilla. L' introduzione di Google ai caratteri variabili sul Web ha una GIF animata che mostra un passaggio tra un voto elevato e nessun voto:

demo animata dei font Amstelvar Alpha con commutazione dell'asse di pendenza


1
Così semplice, così bello.
Randy Hall

18

Ho scoperto che la maggior parte dei caratteri ha le stesse dimensioni quando regoli la spaziatura delle lettere di 1 px.

a {
   letter-spacing: 1px;
}

a:hover {
   font-weight: bold;
   letter-spacing: 0px;
}

Anche se questo cambia il carattere normale in modo che ogni lettera abbia una spaziatura in pixel aggiuntiva. Per i menu i titoli sono così brevi da non presentare alcun problema.


3
questa è la soluzione migliore e più sottovalutata, anche se l'ho modificata in modo che inizi con 0 e vada alla spaziatura tra lettere negative quando in grassetto. inoltre devi metterlo a punto per ogni carattere e dimensione del carattere. Ho anche aggiornato il violino di @ Thorgeir per includere questo (come seconda soluzione) jsfiddle.net/dJcPn/50/
robotik

I browser moderni ora supportano la precisione sub-pixel. Quindi puoi regolare con precisione la spaziatura utilizzando 0.98pxo frazioni più piccole.
Reactgular

Questo sembra un po 'strano poiché l'algoritmo di spaziatura non è esattamente lo stesso (alcune lettere danzano un po') e, cosa più importante, questo non funziona quando i caratteri vengono ridimensionati, anche se converti i 1pxvalori relativi come 0.025exo 0.0125ex. Vedi la mia risposta per una dimostrazione dal vivo.
Adam Katz

@AdamKatz Sospetto che il rendering dei caratteri sia cambiato da quando è stata pubblicata questa risposta e dipende molto dal carattere che stai utilizzando.
Reactgular

5

Per una risposta più aggiornata, puoi utilizzare -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

.element:hover {
  -webkit-text-stroke-width: 1px;
  -webkit-text-stroke-color: black;
}

Questo evita qualsiasi pseudo-elemento (che è un vantaggio per i lettori di schermo) e le ombre del testo (che sembra disordinato e può ancora creare un leggero effetto di "salto") o l'impostazione di larghezze fisse (che può essere poco pratico).

Ti consente anche di impostare un elemento in modo che sia più audace di 1 px (in teoria, puoi creare un carattere in grassetto come preferisci e potrebbe anche essere un allenamento scadente per creare una versione in grassetto di un carattere che non ha un grassetto variante, come i caratteri personalizzati ( modifica: i caratteri variabili svalutano questo suggerimento ). Anche se questo dovrebbe essere evitato in quanto probabilmente farà apparire alcuni caratteri graffianti e frastagliati)

Sicuramente funziona in Edge, Firefox, Chrome e Opera (al momento della pubblicazione) e in Safari ( modifica: @Lars Blumberg, grazie per la conferma ). NON funziona in IE11 o inferiore.

Nota anche che utilizza il -webkitprefisso, quindi questo non è standard e il supporto potrebbe essere abbandonato in futuro, quindi non fare affidamento su questo è il grassetto è davvero importante: è meglio evitare questa tecnica a meno che non sia meramente estetica.


1
Funziona anche su Safari 13.0.5. La soluzione più pulita per me finora.
Lars Blumberg,

4

Sfortunatamente l'unico modo per evitare che la larghezza cambi quando il testo è in grassetto è definire la larghezza dell'elemento dell'elenco, tuttavia, come hai affermato, farlo manualmente richiede tempo e non è scalabile.

L'unica cosa a cui riesco a pensare è utilizzare un po 'di javascript che calcola la larghezza della scheda prima che sia in grassetto, quindi applica la larghezza nello stesso momento in cui è richiesto il grassetto (sia quando passi il mouse o fai clic).


hm, lo sperimenterò, anche se voglio che almeno appaia ragionevole (cioè in grassetto) per gli utenti non JS. Forse posso inviarlo in grassetto, usare JS per decomprimerlo, calcolare la larghezza e poi di nuovo in grassetto? O è solo sciocco?
Mala

Sì, sembra il modo migliore per accogliere gli utenti non javascript.
ajcw

2

Utilizza JavaScript per impostare una larghezza fissa del li in base al contenuto non piegato, quindi grassetto il contenuto applicando uno stile al <a>tag (o aggiungi uno span se <li>non ha figli).


Oops, scusa per averlo ripetuto: $
Dan Blows

Grazie comunque per la tua risposta :)
Mala

@Mala Hai trovato una soluzione? Stranamente, ho un problema simile.
Dan Blows

1

Questa è una domanda molto vecchia, ma la sto rivisitando perché ho riscontrato questo problema in un'app che sto sviluppando e ho trovato tutte le risposte che volevano.

(Salta questo paragrafo per il TL; DR ...)Sto usando il carattere web Gotham da cloud.typography.com e ho pulsanti che iniziano vuoti (con un bordo / testo bianco e uno sfondo trasparente) e acquisiscono un colore di sfondo al passaggio del mouse. Ho scoperto che alcuni dei colori di sfondo che stavo usando non erano in contrasto con il testo bianco, quindi volevo cambiare il testo in nero per quei pulsanti, ma, a causa di un trucco visivo o dei comuni metodi di anti-aliasing, scuro il testo su uno sfondo chiaro sembra sempre essere più leggero del testo bianco su uno sfondo scuro. Ho scoperto che aumentare il peso da 400 a 500 per il testo scuro manteneva quasi esattamente lo stesso peso "visivo". Tuttavia, stava aumentando la larghezza del pulsante di una piccola quantità - una frazione di pixel - ma era sufficiente per far sembrare i pulsanti leggermente "tremolanti", cosa che volevo eliminare.

Soluzione:

Ovviamente, questo è un problema davvero complicato, quindi richiedeva una soluzione complicata. Alla fine ho usato un negativo letter-spacingsul testo più audace come cgTag consigliato sopra, ma 1px sarebbe stato eccessivo, quindi ho calcolato esattamente la larghezza di cui avrei avuto bisogno.

Ispezionando il pulsante in Chrome devtools, ho scoperto che la larghezza predefinita del mio pulsante era 165,47 px e 165,69 px al passaggio del mouse, una differenza di 0,22 px. Il pulsante aveva 9 caratteri, quindi:

0,22 / 9 = 0,024444 px

Convertendolo in unità em, potrei rendere la regolazione della dimensione del carattere indipendente. Il mio pulsante utilizzava una dimensione del carattere di 16 px, quindi:

0,024444 / 16 = 0,001527 em

Quindi, per il mio carattere particolare, il seguente CSS mantiene i pulsanti esattamente della stessa larghezza al passaggio del mouse:

.btn {
  font-weight: 400;
}

.btn:hover {
  font-weight: 500;
  letter-spacing: -0.001527em;
}

Con un po 'di test e utilizzando la formula sopra, puoi trovare esattamente il letter-spacingvalore giusto per la tua situazione e dovrebbe funzionare indipendentemente dalla dimensione del carattere.

L'unico avvertimento è che browser diversi utilizzano calcoli sub-pixel leggermente diversi, quindi se stai mirando a questo livello OCD di precisione sub-pixel perfetta, dovrai ripetere il test e impostare un valore diverso per ogni browser. Gli stili CSS mirati al browser sono generalmente disapprovati , per una buona ragione, ma penso che questo sia un caso d'uso in cui è l'unica opzione che ha senso.


0

Domanda interessante. Suppongo tu stia usando il float, giusto?

Bene, non conosco nessuna tecnica che puoi usare per sbarazzarti dell'ingrandimento di questo font, quindi cercheranno di adattarsi alla larghezza minima richiesta - e variando lo spessore del carattere cambierà questo valore.

L'unica soluzione che conosco per evitare questo cambiamento è quella che hai detto di non volere: impostare le dimensioni fisse su li's.


0

Puoi implementarlo come il menu a comparsa "Acquista per reparto" di amazon.com. Utilizza wide div. Puoi creare un div ampio e nascondere la sua parte destra


0

AGGIORNAMENTO: Ho dovuto usare il tag B per il titolo perché in IE11 la pseudo classe i: after non veniva mostrata quando avevo visibilità: nascosta.

Nel mio caso voglio allineare una casella di controllo / radio di input (progettata su misura) con il testo dell'etichetta in cui il testo diventa in grassetto quando l'input è selezionato.

La soluzione fornita qui non ha funzionato per me in Chrome. L'allineamento verticale dell'input e dell'etichetta è stato incasinato con: dopo che la classe psuedo e -margins non hanno risolto questo problema.

Ecco una soluzione in cui non hai problemi con gli allineamenti verticali.

/* checkbox and radiobutton */
label
{
    position: relative;
    display: inline-block;
    padding-left: 30px;
    line-height: 28px;
}

/* reserve space of bold text so that the container-size remains the same when the label text is set to bold when checked. */
label > input + b + i
{
    font-weight: bold;
    font-style: normal;
    visibility: hidden;
}

label > input:checked + b + i
{
    visibility: visible;
}

/* set title attribute of label > b */
label > input + b:after
{
    display: block;
    content: attr(title);
    font-weight: normal;
    position: absolute;
    left: 30px;
    top: -2px;
    visibility: visible;
}

label > input:checked + b:after
{
    display: none;
}

label > input[type="radio"],
label > input[type="checkbox"]
{
    position: absolute;
    visibility: hidden;
    left: 0px;
    margin: 0px;
    top: 50%;
    transform: translateY(-50%);
}

    label > input[type="radio"] + b,
    label > input[type="checkbox"]  + b
    {
        display: block;
        position: absolute;
        left: 0px;
        margin: 0px;
        top: 50%;
        transform: translateY(-50%);
        width: 24px;
        height: 24px;
        background-color: #00a1a6;
        border-radius: 3px;
    }

    label > input[type="radio"] + b
    {
        border-radius: 50%;
    }

    label > input:checked + b:before
    {
        display: inline-block;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%) rotate(45deg);
        content: '';
        border-width: 0px 3px 3px 0px;
        border-style: solid;
        border-color: #fff;
        width: 4px;
        height: 8px;
        border-radius: 0px;
    }

    label > input[type="checkbox"]:checked + b:before
    {
        transform: translate(-50%, -60%) rotate(45deg);
    }

    label > input[type="radio"]:checked + b:before
    {
        border-width: 0px;
        border-radius: 50%;
        width: 8px;
        height: 8px;
    }
<label><input checked="checked" type="checkbox"/><b title="Male"></b><i>Male</i></label>
<label><input type="checkbox"/><b title="Female"></b><i>Female</i></label>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.