CSS: colocar algum texto em negrito sem alterar o tamanho do contêiner

119

Eu tenho um menu de navegação horizontal, que é basicamente apenas um <ul>com os elementos colocados lado a lado. Não defino largura, mas simplesmente uso preenchimento, porque gostaria que as larguras fossem definidas pela largura do item de menu. Coloco em negrito o item selecionado no momento.

O problema é que, em negrito, a palavra se torna um pouco mais larga, o que faz com que o resto dos elementos se desloquem ligeiramente para a esquerda ou direita. Existe uma maneira inteligente de evitar que isso aconteça? Algo parecido com dizer ao preenchimento para ignorar a largura extra causada pelo negrito? Meu primeiro pensamento foi simplesmente subtrair alguns pixels do preenchimento do elemento "ativo", mas essa quantidade varia.

Se possível, gostaria de evitar definir uma largura estática em cada entrada e, em seguida, centralizar em oposição à solução de preenchimento que tenho atualmente, a fim de simplificar as alterações futuras nas entradas.

Mala
fonte
por que a mudança de tamanho é um problema? Está bagunçando o layout de alguma forma?
Chaulky de
Eu acho que as perguntas de programação web são mais bem feitas no Stack Overflow. Talvez um mod migre isso para lá.
Ciaran,
2
Chaulky: porque há poucas coisas que eu odeio mais, em termos de layout, do que quando os botões que você deveria tentar clicar saltam
Mala,
doejo.com/blog/… é uma solução que descobri que faz exatamente o que John e Blowski estavam falando sobre jscript.
thebulfrog

Respostas:

152

Eu tive o mesmo problema, mas obtive um efeito semelhante com um pequeno compromisso, usei a sombra de texto em seu lugar.

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

Aqui está um exemplo prático:

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>

exemplo jsfiddle

Thorgeir
fonte
51
Adoro esta solução, embora recomende alterá-la para 1px 0px 0px. Parece um pouco mais ousado.
M. Herold,
Eu precisava de uma solução somente css e esta é a resposta.
JCasso de
você é um verdadeiro gênio! a simplicidade o torna incrível!
lufi
1
Apenas adicionar li:hover {text-shadow: 1px 0 0 black;}resultou em texto com muito pouco espaçamento entre as letras. Para melhorar isso novamente, também definimosli {letter-spacing: 1px;}
Patrick Hammer
5
Usei -0.5px 0 #fff, 0.5px 0 #fff, porque pudemos ver a pequena lacuna entre o próprio texto e a sombra. E também quando o texto está em negrito, adiciona peso a ambos os lados.
atorscho
101

A melhor solução de trabalho 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;
}

Ele adiciona um pseudoelemento invisível com largura de texto em negrito, originado por titleatributo.

A text-shadowsolução não parece natural no Mac e não utiliza toda a beleza que a renderização de texto no Mac oferece. :)

http://jsfiddle.net/85LbG/

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

workaholic_gangster911
fonte
11
Esta é a solução mais confiável e limpa , ela simplesmente reserva espaço para texto em negrito no elemento. No meu caso, o pseudo-elemento adicional causou mudança de altura. Adicionar negativo margin-topao ::afterbloco resolveu isso.
Vaclav Novotny
2
Essa solução é ótima! Usei JS para adicionar um atributo "data-content" que contém o texto do elemento para evitar a alteração do HTML.
mistykristie de
1
Impressionante. Esta deve ser a melhor resposta. Observe que, se um elemento diferente de li, você deve usar display: block; no: após o elemento pai.
Blackbam
Ótima solução! Funciona bem com :: before também, no meu caso evita margem adicional na parte inferior.
Thomas Champion
Obrigado por esta solução. Usei inicialmente text-shadow, mas isso não funciona no Safari, pois arredonda os valores decimais. Como eu estava usando 0.1px, isso estava sendo arredondado para 0. Sua solução funcionou perfeitamente. Só substituí o atributo title por name, pois não queria que as dicas de ferramentas aparecessem
Jonathan Cardoz
31

A solução mais portátil e visualmente agradável seria usar text-shadow. Isso revisa e mostra exemplos da resposta de Thorgeir usando os ajustes de Alexxali e meus próprios:

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

Isso coloca pequenas "sombras" em preto (use o nome / código da cor da sua fonte no lugar, blackse necessário) em ambos os lados de cada letra, usando unidades que serão dimensionadas corretamente com a renderização da fonte.

Aviso Aviso: os px valores suportam valores decimais, mas eles não ficarão muito bem quando o tamanho da fonte mudar (por exemplo, o usuário dimensiona a visualização com Ctrl+ +). Em vez disso, use valores relativos.

Essa resposta usa frações de exunidades, pois elas são dimensionadas com a fonte.
Na maioria dos padrões do navegador * , espere 1ex8pxe, portanto, 0.025ex0.1px.

Veja por si mesmo:

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>

Passe o mouse sobre as linhas renderizadas para ver como elas diferem do texto padrão.

Altere o nível de zoom do seu navegador ( Ctrl+ +e Ctrl+- ) para ver como eles variam.

Eu adicionei duas outras soluções aqui para comparação: o truque de espaçamento de letras do @cgTag , que não funciona tão bem, pois envolve adivinhar os intervalos de largura de fonte, e @ workaholic_gangster911's :: após o truque de desenho , que deixa um espaço extra estranho para que o texto em negrito possa se expandir sem cutucar os itens de texto vizinhos (coloquei a atribuição após o texto em negrito para que você possa ver como ele não se move).


No futuro, teremos mais fontes variáveis capazes de coisas como alterar a nota da fonte via font-variation-settings. O suporte do navegador está aumentando (Chrome 63+, Firefox 62+), mas isso ainda requer mais do que apenas fontes padrão e poucas fontes existentes o suportam.

Se você incorporar uma fonte variável, poderá usar CSS da seguinte forma:

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

Há uma demonstração ao vivo com um controle deslizante para tocar com várias notas no Guia de fontes variáveis ​​do Mozilla. A introdução do Google às fontes variáveis ​​na web tem um GIF animado que demonstra a alternância entre uma nota alta e nenhuma nota:

demonstração de fonte Amstelvar Alpha animada com eixo de graduação alternado

Adam Katz
fonte
1
Tão simples, tão bonito.
Randy Hall
18

Descobri que a maioria das fontes tem o mesmo tamanho quando você ajusta o espaçamento entre letras em 1px.

a {
   letter-spacing: 1px;
}

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

Embora isso mude a fonte regular para que cada letra tenha um espaçamento de pixel extra. Para menus, os títulos são tão curtos que não representam um problema.

Reagular
fonte
3
esta é a melhor e mais subestimada solução, embora eu a tenha alterado para que comece com 0 e vá para espaçamento negativo de letras quando em negrito. também é necessário ajustá-lo para cada fonte e tamanho de fonte. Eu também atualizei o violino de @ Thorgeir para incluir este (como a segunda solução) jsfiddle.net/dJcPn/50/
robotik
Os navegadores modernos agora oferecem suporte à precisão de subpixel. Assim, você pode ajustar o espaçamento usando 0.98pxou frações menores.
Reagular de
Isso parece um pouco estranho, pois o algoritmo de espaçamento não é exatamente o mesmo (algumas letras dançam um pouco) e, mais importante, isso não funciona quando as fontes são dimensionadas, mesmo se você converter 1pxpara valores relativos como 0.025exou 0.0125ex. Veja minha resposta para uma demonstração ao vivo.
Adam Katz
@AdamKatz Suspeito que a renderização de fontes mudou desde que esta resposta foi postada, e isso depende muito de qual fonte você está usando.
Reagular em
5

Para obter uma resposta mais atualizada, você pode usar -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

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

Isso evita quaisquer pseudo-elementos (o que é uma vantagem para leitores de tela) e sombras de texto (que parecem confusas e ainda podem criar um leve efeito de 'salto') ou definir qualquer largura fixa (o que pode ser impraticável).

Ele também permite que você defina um elemento para ser mais negrito do que 1px (teoricamente, você pode deixar uma fonte tão negrito quanto quiser e também pode ser um treino de má qualidade para criar uma versão em negrito de uma fonte que não tem negrito variante, como fontes personalizadas ( editar: fontes variáveis ​​depreciam esta sugestão ). Embora isso deva ser evitado, pois provavelmente fará com que algumas fontes pareçam arranhadas e irregulares)

Isso definitivamente funciona no Edge, Firefox, Chrome e Opera (no momento da postagem) e no Safari ( editar: @Lars Blumberg obrigado por confirmar isso ). NÃO funciona no IE11 ou inferior.

Observe também que ele usa o -webkitprefixo, então isso não é padrão e o suporte pode ser abandonado no futuro, então não confie nisso, é muito importante - é melhor evitar essa técnica, a menos que seja meramente estética.

Oliver
fonte
1
Também funciona no Safari 13.0.5. A solução mais limpa para mim até agora.
Lars Blumberg
4

Infelizmente, a única maneira de evitar que a largura mude quando o texto está em negrito é definir a largura do item da lista, no entanto, como você afirmou, fazer isso manualmente é demorado e não escalonável.

A única coisa em que consigo pensar é em usar um javascript que calcula a largura da guia antes de colocá-la em negrito e, em seguida, aplica a largura ao mesmo tempo que o negrito é necessário (quando você passa o mouse ou clica).

ajcw
fonte
hm, vou experimentar isso, embora eu queira que pelo menos pareça razoável (ou seja, em negrito) para não usuários de JS. Talvez eu possa enviar em negrito, usar JS para remover o negrito, calcular a largura e então negrito novamente? Ou isso é apenas bobo?
Mala,
Sim, essa parece ser a melhor maneira de acomodar usuários que não usam JavaScript.
ajcw de
2

Use JavaScript para definir uma largura fixa de li com base no conteúdo sem negrito e, em seguida, coloque o conteúdo em negrito aplicando um estilo à <a>tag (ou adicione um intervalo se o <li>não tiver nenhum filho).

Dan Blows
fonte
Ops - desculpe por repetir: $
Dan Blows
Obrigado pela sua resposta de qualquer maneira :)
Mala
@Mala Você encontrou uma solução? Curiosamente, tenho um problema semelhante.
Dan Blows
1

Esta é uma pergunta muito antiga, mas estou revisando-a porque tive esse problema em um aplicativo que estou desenvolvendo e achei todas as respostas aqui insuficientes.

(Pule este parágrafo para o TL; DR ...)Estou usando a webfont Gotham de cloud.typography.com e tenho botões que começam vazios (com uma borda / texto em branco e um fundo transparente) e adquirem uma cor de fundo ao passar o mouse. Descobri que algumas das cores de fundo que estava usando não contrastavam bem com o texto branco, então eu queria mudar o texto para preto para esses botões, mas - seja por causa de um truque visual ou métodos comuns de suavização - escuro o texto em um fundo claro sempre parece ser mais leve do que o texto branco em um fundo escuro. Descobri que aumentar o peso de 400 para 500 para o texto escuro mantinha quase exatamente o mesmo peso "visual". No entanto, estava aumentando a largura do botão em uma pequena quantidade - uma fração de pixel - mas foi o suficiente para fazer os botões parecerem "tremer" um pouco, do qual eu queria me livrar.

Solução:

Obviamente, este é um problema muito exigente, por isso exigiu uma solução delicada. No final letter-spacingdas contas, usei um negativo no texto mais em negrito, conforme cgTag recomendado acima, mas 1px seria um exagero, então calculei exatamente a largura de que precisaria.

Ao inspecionar o botão no Chrome devtools, descobri que a largura padrão do meu botão era 165,47 px e 165,69 px ao passar o mouse, uma diferença de 0,22 px. O botão tinha 9 caracteres, então:

0,22 / 9 = 0,024444 px

Ao converter isso em unidades em, eu poderia tornar o ajuste do tamanho da fonte agnóstico. Meu botão estava usando um tamanho de fonte de 16 px, então:

0,024444 / 16 = 0,001527em

Portanto, para minha fonte específica, o seguinte CSS mantém os botões exatamente com a mesma largura ao passar o mouse:

.btn {
  font-weight: 400;
}

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

Com alguns testes e usando a fórmula acima, você pode encontrar exatamente o letter-spacingvalor certo para a sua situação, e deve funcionar independentemente do tamanho da fonte.

A única ressalva é que navegadores diferentes usam cálculos de subpixel ligeiramente diferentes, portanto, se você deseja esse nível de OCD de precisão perfeita de subpixel, será necessário repetir o teste e definir um valor diferente para cada navegador. Os estilos CSS direcionados a navegadores geralmente são malvistos , por um bom motivo, mas acho que este é um caso de uso em que é a única opção que faz sentido.

dannymcgee
fonte
0

Pergunta interessante. Suponho que você esteja usando float, certo?

Bem, eu não conheço nenhuma técnica que você possa usar para se livrar desse aumento da fonte, portanto, eles tentarão se ajustar à largura mínima necessária - e a espessura da fonte variando vai alterar esse valor.

A solução única que conheço para evitar essa mudança é uma que você disse que não queria: definir tamanhos fixos para li.

Davis Peixoto
fonte
0

Você pode implementar isso como no menu suspenso "Comprar por departamento" da amazon.com. Ele usa div amplo. Você pode criar um div largo e ocultar sua parte direita

Andrus
fonte
0

ATUALIZAÇÃO: Tive que usar a tag B para o título porque no IE11 a pseudo classe i: after não apareceu quando eu tinha visibilidade: hidden.

No meu caso, quero alinhar uma caixa de seleção / rádio de entrada (com design personalizado) com o texto do rótulo, onde o texto fica em negrito quando a entrada é marcada.

A solução fornecida aqui não funcionou para mim no Chrome. O alinhamento vertical da entrada e do rótulo bagunçou com: after psuedo class e -margins não corrigiram isso.

Aqui está uma correção em que você não terá problemas com alinhamentos verticais.

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