CSS:在不更改其容器大小的情况下加粗一些文本


119

我有一个水平导航菜单,它基本上只是一个<ul>并排设置的元素。我不定义宽度,而只是使用填充,因为我希望宽度由菜单项的宽度定义。我加粗了当前选择的项目。

麻烦在于,粗体字会稍微变宽,从而导致其余元素向左或向右移动。有防止这种情况发生的聪明方法吗?告诉填充忽略粗体导致的额外宽度的方法是什么?我的第一个想法是简单地从“活动”元素的填充中减去几个像素,但是这个数量是变化的。

如果可能的话,我想避免在每个条目上设置静态宽度,然后将其居中(与我目前使用的填充解决方案相对),以使将来对条目的更改变得简单。


为什么尺寸改变是一个问题?是否以某种方式弄乱了布局?
Chaulky 2011年

我认为最好在Stack Overflow上提出Web编程问题。也许一个mod会在那里迁移它。
Ciaran

2
Chaulky:因为有我恨多,几件事情布局明智的,比当你应该按钮尝试点击跳来跳去
马拉

doejo.com/blog/…是我发现的一种解决方案,完全符合John和Blowski所说的jscript。
thebulfrog

Answers:


152

我遇到了同样的问题,但是在折衷上取得了类似的效果,我改用了文本阴影。

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

这是一个工作示例:

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示例


51
我喜欢这种解决方案,尽管我建议将其翻转为1px 0px 0px。看起来有点大胆。
M. Herold

我需要一个仅CSS的解决方案,这就是答案。
JCasso 2013年

你真是个天才!简单使其令人敬畏!
lufi 2014年

1
仅添加会li:hover {text-shadow: 1px 0 0 black;}导致文本之间的字母间距太小。为了再次改善这一点,我们还设置了li {letter-spacing: 1px;}
Patrick Hammer

5
我使用-0.5px 0 #fff, 0.5px 0 #fff,因为我们可以看到文本本身和阴影之间的微小缝隙。而且,当文本为粗体时,会在两侧增加粗细。
atorsscho

101

使用:: 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;
}

它添加了一个不可见的伪元素,其宽度为粗体,由title属性提供。

text-shadow解决方案在Mac上看起来不自然,并且没有利用Mac上文本呈现所提供的所有优点。.:)

http://jsfiddle.net/85LbG/

信用:https : //stackoverflow.com/a/20249560/5061744


11
这是最可靠,最干净的解决方案,它只是为元素中的粗体文本保留了空间。在我的情况下,附加的伪元素引起了高度的变化。margin-top::after模块中添加负数可以解决该问题。
瓦茨拉夫·诺沃特尼

2
这个解决方案很棒!我使用JS添加了一个“数据内容”属性,该属性包含元素的文本,因此可以避免更改HTML。
mistykristie

1
太棒了 这应该是最好的答案。请注意,如果元素与li不同,则可能必须使用display:block;。在:after父元素上。
Blackbam

很好的解决方案!在:: before上也可以正常工作,在我的情况下,这可以防止底部额外的边距。
托马斯·冠军

感谢您的解决方案。我最初使用text-shadow,但这在Safari中不起作用,因为它舍入了十进制值。由于我使用的是0.1px,因此将其四舍五入为0。您的解决方案可以完美地工作。我只用名称替换了标题属性,因为我不想显示工具提示
Jonathan Cardoz '18

31

最便携和视觉上令人愉悦的解决方案是使用text-shadow。这修改并显示了使用Alexxali和我自己的调整对Thorgeir的回答的示例:

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

这会在black每个字母的两侧都使用会随字体渲染正确缩放的单位在黑色的黑色阴影(如果需要,请使用字体的颜色名称/代码代替)。

警告 警告: px值确实支持十进制值,但是当字体大小更改时它们看起来不会太大(例如,用户使用Ctrl+ 缩放视图+)。请使用相对值。

此答案使用ex单位的分数,因为它们随字体缩放。
在大多数浏览器默认*中,期望1ex8px,因此期望0.025ex0.1px

你自己看:

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>

将鼠标悬停在渲染的线条上可以查看它们与标准文本的区别。

更改浏览器的缩放级别(Ctrl+ +Ctrl+ -)以查看它们如何变化。

我在这里添加了两个其他解决方案进行比较:@cgTag的字母间距技巧,因为涉及猜测字体宽度范围,所以效果不佳;以及@ workaholic_gangster911的:: after绘画技巧,它留下了笨拙的额外空间,因此粗体文本可以扩展而不用拖累邻近的文本项(我将属性放在粗体之后,这样您就可以看到它如何移动)。


将来,我们将提供更多的可变字体,这些字体可以通过更改字体等级font-variation-settings浏览器的支持正在增加(Chrome 63 +,Firefox 62+),但这不仅需要标准字体而且还需要很少的现有字体支持。

如果您嵌入可变字体,则可以使用如下CSS:

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

Mozilla可变字体指南中有一个带滑块的实时演示,可以按各种等级播放。Google的网络上可变字体简介中有一个动画GIF,展示了高等级和无等级之间的切换:

带有切换坡度轴的动画Amstelvar Alpha字体演示


1
如此简单,如此美丽。
兰迪·霍尔

18

当您将字母间距调整1px时,我发现大多数字体都具有相同的大小。

a {
   letter-spacing: 1px;
}

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

虽然这确实会更改常规字体,但每个字母都有一个额外的像素间距。对于菜单,标题太短了,不会出现问题。


3
这是最好的,也是最被低估的解决方案,尽管我对其进行了更改,以使其从0开始并在加粗时变为负字母间距。您还必须针对每种字体和字体大小对其进行微调。我还更新了@Thorgeir的小提琴,以包括此(作为第二个解决方案)jsfiddle.net/dJcPn/50/
robotik

现代浏览器现在支持亚像素精度。因此,您可以使用0.98px或更小的分数微调间距。
Reactgular '16

这看起来有点奇怪,因为间距算法是不完全一样的(一些字母跳舞了一下周围),更重要的是,当字体缩放,这并不工作,即使你转换1px为相对值类似0.025ex0.0125ex。请参阅我的答案进行现场演示。
亚当·卡兹

@AdamKatz我怀疑自从发布此答案以来,字体渲染已经改变,并且在很大程度上取决于您使用的是哪种字体。
Reactgular

5

有关最新答案,可以使用-webkit-text-stroke-width

.element {
  font-weight: normal;
}

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

这样可以避免使用任何伪元素(对于屏幕阅读器来说是一个加号)和文本阴影(看起来很凌乱,仍然可以产生轻微的“跳跃”效果)或设置任何固定宽度(这可能是不切实际的)。

它还允许您将元素设置为大于1px(从理论上讲,您可以将字体设置为喜欢的粗体,也可以作为劣质的练习来创建没有粗体的字体变体,例如自定义字体(编辑:可变字体会降低此建议)。尽管应避免这种情况,因为这可能会使某些字体显得划痕和锯齿。

我肯定可以在Edge,Firefox,Chrome和Opera(发布时)和Safari中使用它编辑:@Lars Blumberg,感谢您确认)。在IE11或更低版本中不起作用。

还要注意,它使用-webkit前缀,因此这不是标准的,将来可能会放弃支持,因此不要依赖于此是很重要的-最好避免使用此技术,除非它只是美观。


1
也可以在Safari 13.0.5上使用。到目前为止对我来说最干净的解决方案。
拉尔斯·布隆伯格

4

不幸的是,当文本为粗体时,避免宽度变化的唯一方法是定义列表项的宽度,但是,正如您所说的那样,手动进行此操作很耗时且不可扩展。

我唯一能想到的就是使用一些JavaScript,在粗体显示之前先计算选项卡的宽度,然后在需要粗体的同时应用宽度(当您悬停或单击时)。


嗯,尽管我希望它对于非JS用户来说至少看起来合理(即加粗),但我将对此进行试验。也许我可以将其发送为粗体,使用JS对其进行粗体处理,计算宽度,然后再次对其进行粗体处理?还是只是愚蠢?
马拉

是的,这似乎是容纳非JavaScript用户的最佳方法。
2011年

2

使用JavaScript根据未粗体化的内容设置li的固定宽度,然后通过对<a>标签应用样式来加粗内容(如果<li>没有子元素,则添加跨度)。


糟糕-非常抱歉:$
Dan Blows'Apr

无论如何,谢谢您的回答:)
Mala

@Mala您找到解决方案了吗?有趣的是,我有一个类似的问题。
Dan Blows

1

这是一个非常老的问题,但是我正在重新研究它,因为我在正在开发的应用程序中遇到了这个问题,并在此找到了所有想要的答案。

(将本段略为TL; DR ...)我正在使用cloud.typography.com上的Gotham网络字体,并且有一些按钮从空心开始(带有白色边框/文本和透明背景)并在悬停时获取背景颜色。我发现我使用的某些背景颜色与白色文本无法很好地形成对比,因此我想将这些按钮的文本更改为黑色,但是-由于视觉技巧或常用的抗锯齿方法,所以颜色较暗浅色背景上的文字总是比深色背景上的白色文字轻。我发现深色文字的权重从400增加到500几乎保持了相同的“视觉”权重。但是,它只是将按钮的宽度增加了很小的一部分(一个像素的几分之一),但这足以使按钮看起来有些“抖动”,这是我想要消除的。

解:

显然,这是一个非常棘手的问题,因此需要一个棘手的解决方案。最终,我letter-spacing对上面推荐的cgTag 使用了一个较粗体的负号,但是1px可能会显得过大,所以我只计算了我需要的宽度。

通过检查Chrome devtools中的按钮,我发现按钮的默认宽度为165.47像素,悬停时为165.69像素,相差0.22像素。该按钮有9个字符,因此:

0.22 / 9 = 0.024444px

通过将其转换为em单位,我可以使调整字体大小不可知。我的按钮使用的字体大小为16px,所以:

0.024444 / 16 = 0.001527em

因此,对于我的特定字体,以下CSS使按钮在悬停时保持完全相同的宽度:

.btn {
  font-weight: 400;
}

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

通过稍作测试并使用上述公式,您可以找到letter-spacing适合自己情况的正确值,并且无论字体大小如何,该值都可以工作。

一个警告是,不同的浏览器使用的子像素计算略有不同,因此,如果要达到OCD级别的亚像素完美精度,则需要重复测试并为每个浏览器设置不同的值。基于浏览器的CSS样式通常由于种种原因而皱眉,但我认为这是一个用例,它是唯一有意义的选择。


0

有趣的问题。我想您正在使用float,对吗?

好吧,我不知道您可以使用任何技术来消除这种字体放大问题,因此它们将尝试适应所需的最小宽度-改变字体粗细会更改该值。

我知道避免这种变化的唯一解决方案是您不希望使用的解决方案:将固定尺寸设置为li。


0

您可以像amazon.com的“按部门购物”悬停菜单那样实现。它使用div宽。您可以创建宽div并隐藏其正确的部分


0

更新:必须使用B标记作为标题,因为在IE11中,伪类i:after在我具有visible:hidden时没有显示。

在我的情况下,我想将(自定义设计的)输入复选框/单选框与标签文本对齐,其中选中输入后文本将变为粗体。

此处提供的解决方案不适用于Chrome。输入和标签的垂直对齐弄乱了:after psuedo类,-margins不能解决这个问题。

这是一个修复程序,您不会在垂直对齐方面遇到麻烦。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.