CSS: poner texto en negrita sin cambiar el tamaño de su contenedor

119

Tengo un menú de navegación horizontal, que es básicamente un <ul>conjunto de elementos uno al lado del otro. No defino el ancho, sino que simplemente uso el relleno, porque me gustaría que los anchos se definan por el ancho del elemento del menú. Pongo en negrita el elemento seleccionado actualmente.

El problema es que en negrita, la palabra se vuelve un poco más ancha, lo que hace que el resto de los elementos se desplace ligeramente hacia la izquierda o hacia la derecha. ¿Existe alguna forma inteligente de evitar que esto suceda? ¿Algo como decirle al acolchado que ignore el ancho adicional causado por la negrita? Mi primer pensamiento fue simplemente restar algunos píxeles del relleno del elemento "activo", pero esta cantidad varía.

Si es posible, me gustaría evitar establecer un ancho estático en cada entrada y luego centrarlo en lugar de la solución de relleno que tengo actualmente, para simplificar los cambios futuros en las entradas.

Mala
fuente
¿Por qué es un problema el cambio de tamaño? ¿Está estropeando el diseño de alguna manera?
Chaulky
Creo que la pregunta de programación web se hace mejor en Stack Overflow. Tal vez un mod migre esto allí.
Ciaran
2
Chaulky: porque hay pocas cosas que odio más, en cuanto al diseño, que cuando se supone que debes intentar hacer clic en los botones para saltar
Mala
doejo.com/blog/… es una solución que he encontrado que hace exactamente lo que John y Blowski estaban hablando de jscript.
thebulfrog

Respuestas:

152

Tuve el mismo problema, pero obtuve un efecto similar con un pequeño compromiso, usé text-shadow en su lugar.

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

Aquí hay un ejemplo de trabajo:

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>

ejemplo de jsfiddle

Thorgeir
fuente
51
Me encanta esta solución, aunque recomiendo darle la vuelta a 1px 0px 0px. Parece un poco más atrevido.
M. Herold
Necesitaba una solución solo de CSS y esta es la respuesta.
JCasso
eres un verdadero genio! la simplicidad lo hace asombroso!
lufi
1
Solo agregar li:hover {text-shadow: 1px 0 0 black;}dio como resultado un texto con muy poco espacio entre las letras. Para mejorar eso nuevamente, también configuramosli {letter-spacing: 1px;}
Patrick Hammer
5
Usé -0.5px 0 #fff, 0.5px 0 #fff, porque podíamos ver el pequeño espacio entre el texto en sí y la sombra. Y también cuando el texto está en negrita, agrega peso a ambos lados.
atorscho
101

La mejor solución de trabajo usando :: 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;
}

Agrega un pseudoelemento invisible con un ancho de texto en negrita, obtenido por titleatributo.

La text-shadowsolución parece poco natural en Mac y no utiliza toda la belleza que ofrece la representación de texto en Mac .. :)

http://jsfiddle.net/85LbG/

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

workaholic_gangster911
fuente
11
Esta es la solución más confiable y limpia , simplemente reserva espacio para texto en negrita en el elemento. En mi caso, el pseudoelemento adicional provocó un cambio de altura. Agregar negativo margin-topal ::afterbloque resolvió eso.
Vaclav Novotny
2
¡Esta solución es genial! Usé JS para agregar un atributo de "contenido de datos" que contiene el texto del elemento para evitar cambiar el HTML.
mistykristie
1
Increíble. Esta debería ser la mejor respuesta. Tenga en cuenta que si es un elemento diferente de li, es posible que deba usar display: block; en el elemento padre: after.
Blackbam
¡Gran solución! Funciona bien con :: antes también, en mi caso esto evita un margen adicional en la parte inferior.
Thomas Champion
Gracias por esta solucion Inicialmente usé text-shadow, pero esto no funciona en Safari ya que redondea los valores decimales. Como estaba usando 0.1px, esto se redondeaba a 0. Su solución funcionó a la perfección. Solo reemplacé el atributo de título con el nombre porque no quería que aparecieran las descripciones emergentes
Jonathan Cardoz
31

La solución más portátil y visualmente agradable sería usar text-shadow. Esto revisa y muestra ejemplos de la respuesta de Thorgeir usando los ajustes de Alexxali y los míos:

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

Esto pone pequeñas "sombras" en negro (use el nombre / código de color de su fuente en lugar de blacksi es necesario) en ambos lados de cada letra usando unidades que se escalarán correctamente con la representación de la fuente.

advertencia Advertencia: los px valores admiten valores decimales, pero no se verán tan bien cuando cambia el tamaño de fuente (por ejemplo, el usuario escala la vista con Ctrl+ +). En su lugar, utilice valores relativos.

Esta respuesta usa fracciones de exunidades, ya que se escalan con la fuente.
En ~ la mayoría de los navegadores predeterminados * , espere 1ex8pxy por lo tanto 0.025ex0.1px.

Ver por ti mismo:

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>

Pase el cursor sobre las líneas renderizadas para ver en qué se diferencian del texto estándar.

Modifique el nivel de zoom de su navegador ( Ctrl+ +y Ctrl+ -) para ver cómo varían.

Agregué otras dos soluciones aquí para comparar: el truco de espaciado de letras de @ cgTag , que no funciona tan bien ya que implica adivinar rangos de ancho de fuente, y el truco de dibujo :: after de @ workaholic_gangster911 , que deja un espacio extra incómodo para que el texto en negrita pueda expandirse sin empujar los elementos de texto vecinos (coloco la atribución después del texto en negrita para que pueda ver cómo no se mueve).


En el futuro, tendremos más fuentes variables capaces de cosas como cambiar el grado de fuente a través de font-variation-settings. La compatibilidad con el navegador está aumentando (Chrome 63+, Firefox 62+), pero esto aún requiere más que solo fuentes estándar y pocas fuentes existentes lo admiten.

Si incrusta una fuente variable, podrá usar CSS de esta manera:

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

Hay una demostración en vivo con un control deslizante para jugar con varios grados en la Guía de fuentes variables de Mozilla. La Introducción de Google a las fuentes variables en la web tiene un GIF animado que muestra un cambio entre una calificación alta y ninguna calificación:

Demostración animada de la fuente Amstelvar Alpha con alternancia del eje de pendiente

Adam Katz
fuente
1
Tan simple, tan hermoso.
Randy Hall
18

Descubrí que la mayoría de las fuentes tienen el mismo tamaño cuando ajusta el espacio entre letras en 1 px.

a {
   letter-spacing: 1px;
}

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

Si bien esto cambia la fuente normal para que cada letra tenga un espacio extra entre píxeles. Para los menús, los títulos son tan cortos que no presentan ningún problema.

Reactgular
fuente
3
esta es la mejor y más subestimada solución, aunque la cambié para que comience con 0 y vaya a un espacio entre letras negativo cuando esté en negrita. también tienes que ajustarlo para cada fuente y tamaño de fuente. También actualicé el violín de @ Thorgeir para incluir esto (como la segunda solución) jsfiddle.net/dJcPn/50/
robotik
Los navegadores modernos ahora admiten una precisión de subpíxeles. Por lo que puede ajustar el espaciado utilizando 0.98pxfracciones más pequeñas.
Reactgular
Esto parece un poco extraño ya que el algoritmo de espaciado no es exactamente el mismo (algunas letras bailan un poco) y, lo que es más importante, esto no funciona cuando las fuentes se escalan, incluso si convierte 1pxa valores relativos como 0.025exo 0.0125ex. Vea mi respuesta para una demostración en vivo.
Adam Katz
@AdamKatz Sospecho que la representación de fuentes ha cambiado desde que se publicó esta respuesta, y depende mucho de la fuente que estés usando.
Reactgular
5

Para obtener una respuesta más actualizada, puede utilizar -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

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

Esto evita cualquier pseudoelemento (que es una ventaja para los lectores de pantalla) y sombras de texto (que se ve desordenado y aún puede crear un ligero efecto de 'salto') o establecer anchos fijos (que pueden ser poco prácticos).

También le permite configurar un elemento para que sea más audaz que 1px (teóricamente, puede hacer una fuente tan negrita como desee y también podría ser un ejercicio de mala calidad para crear una versión en negrita de una fuente que no tiene negrita variante, como fuentes personalizadas ( editar: las fuentes variables deprecian esta sugerencia ). Aunque esto debe evitarse ya que probablemente hará que algunas fuentes parezcan ásperas e irregulares)

Definitivamente esto funciona en Edge, Firefox, Chrome y Opera (en el momento de la publicación) y en Safari ( editar: @Lars Blumberg, gracias por confirmarlo ). NO funciona en IE11 o inferior.

También tenga en cuenta que utiliza el -webkitprefijo, por lo que esto no es estándar y el soporte puede ser eliminado en el futuro, así que no confíe en esto, es audaz es realmente importante; es mejor evitar esta técnica a menos que sea meramente estética.

Oliver
fuente
1
También funciona en Safari 13.0.5. La solución más limpia para mí hasta ahora.
Lars Blumberg
4

Desafortunadamente, la única forma de evitar que el ancho cambie cuando el texto está en negrita es definir el ancho del elemento de la lista; sin embargo, como dijiste, hacerlo manualmente consume mucho tiempo y no es escalable.

Lo único que se me ocurre es usar un javascript que calcula el ancho de la pestaña antes de que esté en negrita, y luego aplica el ancho al mismo tiempo que se requiere el negrita (ya sea al pasar el mouse o al hacer clic).

ajcw
fuente
hm, experimentaré con esto, aunque quiero que al menos se vea razonable (es decir, en negrita) para los usuarios que no son de JS. Tal vez pueda enviarlo en negrita, usar JS para desmarcarlo, calcular el ancho y luego volverlo a poner en negrita. ¿O es una tontería?
Mala
Sí, parece que es la mejor forma de adaptarse a los usuarios que no utilizan JavaScript.
ajcw
2

Use JavaScript para establecer un ancho fijo de li basado en el contenido sin negrita, luego en negrita el contenido aplicando un estilo a la <a>etiqueta (o agregue un intervalo si <li>no tiene hijos).

Dan golpes
fuente
Vaya, perdón por repetir: $
Dan Blows
Gracias por tu respuesta de todos modos :)
Mala
@Mala ¿Encontraste una solución? Curiosamente, tengo un problema similar.
Dan Blows
1

Esta es una pregunta muy antigua, pero la estoy revisando porque tuve este problema en una aplicación que estoy desarrollando y encontré todas las respuestas aquí.

(Omita este párrafo para el TL; DR ...)Estoy usando la fuente web Gotham de cloud.typography.com, y tengo botones que comienzan en forma hueca (con un borde / texto blanco y un fondo transparente) y adquieren un color de fondo al pasar el mouse. Descubrí que algunos de los colores de fondo que estaba usando no contrastaban bien con el texto blanco, así que quería cambiar el texto a negro para esos botones, pero, ya sea por un truco visual o métodos comunes de suavizado, oscuro el texto sobre un fondo claro siempre parece tener un peso más ligero que el texto blanco sobre un fondo oscuro. Descubrí que aumentar el peso de 400 a 500 para el texto oscuro mantenía casi exactamente el mismo peso "visual". Sin embargo, estaba aumentando el ancho del botón en una pequeña cantidad, una fracción de un píxel, pero fue suficiente para hacer que los botones parecieran "temblar" levemente, de lo que quería deshacerme.

Solución:

Obviamente, este es un problema realmente delicado, por lo que requería una solución delicada. En última instancia, utilicé un negativo letter-spacingen el texto más audaz como cgTag recomendó anteriormente, pero 1px habría sido demasiado, así que calculé exactamente el ancho que necesitaría.

Al inspeccionar el botón en las herramientas de desarrollo de Chrome, encontré que el ancho predeterminado de mi botón era 165.47px y 165.69px al pasar el mouse, una diferencia de 0.22px. El botón tenía 9 caracteres, entonces:

0.22 / 9 = 0.024444px

Al convertir eso a unidades em, podría hacer que el ajuste del tamaño de fuente fuera independiente. Mi botón estaba usando un tamaño de fuente de 16px, entonces:

0.024444 / 16 = 0.001527em

Entonces, para mi fuente particular, el siguiente CSS mantiene los botones exactamente del mismo ancho al pasar el mouse:

.btn {
  font-weight: 400;
}

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

Con algunas pruebas y utilizando la fórmula anterior, puede encontrar exactamente el letter-spacing valor para su situación, y debería funcionar independientemente del tamaño de fuente.

La única advertencia es que los diferentes navegadores usan cálculos de subpíxeles ligeramente diferentes, por lo que si está apuntando a este nivel de OCD de precisión perfecta de subpíxeles, deberá repetir la prueba y establecer un valor diferente para cada navegador. Los estilos CSS orientados al navegador generalmente están mal vistos , por una buena razón, pero creo que este es un caso de uso en el que es la única opción que tiene sentido.

dannymcgee
fuente
0

Interesante pregunta. Supongo que estás usando flotación, ¿verdad?

Bueno, no conozco ninguna técnica que pueda usar para deshacerse de esta ampliación de fuente, por lo tanto, intentarán ajustarse al ancho mínimo requerido, y la variación del grosor de la fuente cambiará este valor.

La solución única que conozco para evitar este cambio es una que dijiste que no querías: establecer tamaños fijos en li.

Davis Peixoto
fuente
0

Puede implementar esto como el menú flotante "Comprar por departamento" de amazon.com. Utiliza div ancho. Puede crear div ancho y ocultar su parte derecha

Andrus
fuente
0

ACTUALIZACIÓN: Tuve que usar la etiqueta B para el título porque en IE11 la pseudoclase i: after no se mostraba cuando tenía visibilidad: oculta.

En mi caso, quiero alinear una casilla de verificación / radio de entrada (diseñada a medida) con el texto de la etiqueta donde el texto se pone en negrita cuando se marca la entrada.

La solución proporcionada aquí no me funcionó en Chrome. La alineación vertical de entrada y etiqueta se estropeó con la clase: after psuedo y -margins no solucionó esto.

Aquí hay una solución en la que no tiene problemas con las alineaciones verticales.

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

Erik
fuente