web-dev-qa-db-fra.com

CSS: mettre du texte en gras sans changer la taille de son conteneur

J'ai un menu de navigation horizontal, qui est fondamentalement juste un <ul> avec les éléments placés côte à côte. Je ne définis pas la largeur, mais utilise simplement le rembourrage, car j'aimerais que les largeurs soient définies par la largeur de l'élément de menu. J'ose en gras l'élément actuellement sélectionné.

Le problème est que, en caractères gras, la Parole devient légèrement plus large, ce qui entraîne un léger décalage du reste des éléments vers la gauche ou la droite. Y a-t-il un moyen intelligent d'empêcher que cela se produise? Quelque chose dans le sens de dire au rembourrage d'ignorer la largeur supplémentaire causée par le gras? Ma première idée a été de simplement soustraire quelques pixels du remplissage de l’élément "actif", mais cette quantité varie.

Si possible, j'aimerais éviter de définir une largeur statique sur chaque entrée, puis de centrer par opposition à la solution de remplissage que j'ai actuellement, afin de simplifier les modifications futures des entrées.

68
Mala

J'ai eu le même problème, mais j'ai eu un effet similaire avec un petit compromis, j'ai utilisé text-shadow à la place. 

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

Voici un exemple de travail:

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>

exemple jsfiddle

111
Thorgeir

La meilleure solution de travail en utilisant :: après

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;
}

Il ajoute un pseudo-élément invisible avec une largeur en gras, généré par l'attribut title.

La solution text-shadow ne semble pas naturelle sur Mac et n'utilise pas toute la beauté du rendu de texte sur Mac .. :)

http://jsfiddle.net/85LbG/

Crédit: https://stackoverflow.com/a/20249560/5061744

71

La solution la plus portable et la plus agréable visuellement serait d’utiliser text-shadow . Ceci révise et montre des exemples de réponse de Thorgeir en utilisant Alexxali et mes propres réglages:

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

Cela place de minuscules "ombres" en noir (utilisez le nom/code de couleur de votre police à la place de black si nécessaire) des deux côtés de chaque lettre en utilisant des unités qui évolueront correctement avec le rendu de la police.

warning  Avertissement: Les valeurs px prennent en charge les valeurs décimales, mais elles n’auront pas une aussi belle apparence lorsque la taille de la police change (par exemple, l'utilisateur redimensionne la vue avec Ctrl++). Utilisez des valeurs relatives à la place.

Cette réponse utilise des fractions de ex car elles s’adaptent à la police.
Dans ~ la plupart des paramètres par défaut du navigateur * , attendez 1ex8px et donc 0.025ex0.1px.

Voir par vous-même:

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>

Survolez les lignes affichées pour voir en quoi elles diffèrent du texte standard.

Modifiez le niveau de zoom de votre navigateur (Ctrl++ et Ctrl+-) pour voir comment ils varient.

J'ai ajouté deux autres solutions ici à titre de comparaison: astuce de l'espacement des lettres de @ cgTag , qui ne fonctionne pas très bien, car elle implique de deviner les plages de largeur de police, et astuce de @ after workikolic @ workaholic_gangster911 , ce qui laisse un espace supplémentaire inconfortable pour que le texte en gras puisse s'agrandir sans déplacer les éléments de texte voisins (je mets l'attribution après le texte en gras afin que vous puissiez voir comment il ne bouge pas).


À l'avenir, nous aurons plus de polices variables capables de choses comme changer le niveau de police via font-variation-settings. Prise en charge du navigateur est en train de monter en puissance (Chrome 63+, Firefox 62+), mais cela nécessite toujours plus que des polices standard et peu de polices existantes la prennent en charge.

Si vous incorporez une police de variable, vous pourrez utiliser CSS comme ceci:

/* 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; }
}

Il existe un démo en direct avec un curseur pour jouer avec différentes notes dans le Guide des polices variables de Mozilla. Google Introduction aux polices de caractères variables sur le Web possède un fichier GIF animé illustrant une bascule entre une note élevée et aucune note:

animated Amstelvar Alpha font demo with toggling grade axis

21
Adam Katz

J'ai constaté que la plupart des polices ont la même taille lorsque vous ajustez l'espacement des lettres de 1 px.

a {
   letter-spacing: 1px;
}

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

Bien que cela change la police normale afin que chaque lettre ait un espacement supplémentaire de pixels. Pour les menus, les titres sont si courts que cela ne pose pas de problème.

14
cgTag

Malheureusement, le seul moyen d'éviter que la largeur ne change lorsque le texte est en gras est de définir la largeur de l'élément de la liste. Toutefois, comme vous l'avez indiqué manuellement, cela prend beaucoup de temps et n'est pas évolutif.

La seule chose à laquelle je peux penser est d'utiliser du javascript qui calcule la largeur de l'onglet avant qu'il ne soit en gras, puis l'applique en même temps que le gras est requis (que vous soyez en survol ou en cliquant).

3
ajcw

Pour une réponse plus à jour, vous pouvez utiliser -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

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

Cela évite les pseudo-éléments (ce qui est un avantage pour les lecteurs d'écran) et les ombres de texte (ce qui semble désordonné et peut encore créer un léger effet de "saut") ou le réglage de largeurs fixes (ce qui peut être impraticable).

Il vous permet également de définir un élément plus audacieux que 1px (en théorie, vous pouvez créer une police aussi grasse que vous le souhaitez et vous pourriez également vous entraîner de manière approximative pour créer une version audacieuse d’une police qui n’a pas de caractère gras. comme des polices personnalisées, bien que cela devrait être évité, car certaines polices apparaîtront probablement éraflées et dentelées)

Cela fonctionne certainement dans les versions les plus récentes d'Edge, Firefox, Chrome et Opera. Je suppose que cela fonctionne dans Safari (Opera en est généralement un bon baromètre), même si je ne l’ai pas testé sur Safari. Cela ne fonctionne PAS dans IE11 ou inférieur.

Notez également qu'il utilise le préfixe -webkit, ce qui n'est donc pas standard et que le support risque d'être abandonné à l'avenir. Ne vous fiez pas à cela, c'est audacieux, c'est vraiment important - il est préférable d'éviter cette technique, sauf si elle est simplement esthétique. .

1
Oliver

Utilisez JavaScript pour définir une largeur fixe du li en fonction du contenu non gras, puis mettez le contenu en gras en appliquant un style à la balise <a> (ou ajoutez une étendue si le <li> n'a aucun enfant).

1
Dan Blows

Vous pouvez implémenter ceci comme le menu déroulant "Shop by department" d'Amazon.com. Il utilise large div. Vous pouvez créer large div et cacher sa partie droite

0
Andrus

UPDATE: Il a fallu utiliser la balise B pour le titre car dans IE11, la pseudo classe i: after ne s'affichait pas lorsque j'avais la visibilité: hidden.

Dans mon cas, je veux aligner une case à cocher/radio d'entrée (conçue sur mesure) avec le texte de l'étiquette où le texte est en gras lorsque l'entrée est cochée.

La solution fournie ici ne fonctionnait pas pour moi dans Chrome. L’alignement vertical de l’entrée et de l’étiquette a été gâché par le: après que psuedo class et -margins n’aient pas résolu le problème.

Voici une solution pour laquelle vous ne rencontrez aucun problème d'alignement vertical.

/* 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>

0
Erik

C'est une très vieille question, mais j'y reviens car j'ai eu ce problème dans une application que je développe et j'ai trouvé toutes les réponses ici voulues.

(Ignorez ce paragraphe pour le TL; DR ...) J'utilise le forum Web Gotham de cloud.typography.com, et j'ai des boutons qui commencent en creux (avec une bordure blanche/un texte et un arrière-plan transparent) et acquérir une couleur de fond en vol stationnaire. J'ai constaté que certaines des couleurs d'arrière-plan que j'utilisais ne contrastaient pas bien avec le texte en blanc. Je souhaitais donc modifier le texte en noir pour ces boutons, mais que ce soit à cause d'une astuce visuelle ou de méthodes d'anti-aliasing standard le texte sur un fond clair semble toujours être plus léger que le texte blanc sur un fond sombre. J'ai trouvé que l'augmentation du poids de 400 à 500 pour le texte sombre maintenait presque exactement le même poids "visuel". Cependant, cela augmentait légèrement la largeur des boutons - une fraction de pixel - mais cela suffisait à faire en sorte que les boutons apparaissent un peu "instables", ce que je voulais supprimer.

Solution:

De toute évidence, il s’agit d’un problème très complexe, qui a donc nécessité une solution minutieuse. En fin de compte, j’ai utilisé un letter-spacing négatif sur le texte plus gras, comme recommandé par cgTag ci-dessus, mais 1px aurait été excessif, alors j’ai calculé exactement la largeur dont j’aurais besoin.

En inspectant le bouton dans Chrome devtools, j'ai constaté que la largeur par défaut de mon bouton était de 165,47 pixels, et de 165,69 pixels en vol stationnaire, une différence de 0,22 pixels. Le bouton avait 9 caractères, donc:

0,22/9 = 0,024444px

En convertissant cela en unités em, je pouvais effectuer un ajustement indépendant de la taille de la police. Mon bouton utilisait une taille de police de 16px, donc:

0,024444/16 = 0,001527em

Donc, pour ma police particulière, le CSS suivant garde les boutons exactement la même largeur en survol:

.btn {
  font-weight: 400;
}

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

Avec un peu de test et en utilisant la formule ci-dessus, vous pouvez trouver exactement la valeur letter-spacing correspondant à votre situation, et cela devrait fonctionner quelle que soit la taille de la police.

Le seul inconvénient est que différents navigateurs utilisent des calculs de sous-pixels légèrement différents. Par conséquent, si vous visez ce niveau de précision OCD parfaite au niveau des sub-pixels, vous devrez répéter les tests et définir une valeur différente pour chaque navigateur. Les styles CSS ciblés sur les navigateurs sont généralement mal vus , pour une bonne raison, mais je pense que c'est un cas d'utilisation où c'est la seule option logique.

0
dannymcgee

Question interessante. Je suppose que vous utilisez float, non?

Eh bien, je ne connais aucune technique que vous puissiez utiliser pour vous débarrasser de cet agrandissement de police. Ils essaieront donc de s’adapter à la largeur minimale requise - et une épaisseur de police variable modifiera cette valeur.

La solution unique que je connaisse pour éviter ce changement est celle que vous avez déclarée ne pas vouloir: définir des tailles fixes comme étant des li.

0
Dave