CSS: text offset without resizing the container

I have a horizontal navigation menu that is basically just <ul> with elements located side by side. I do not define the width, but just use padding because I would like the width to be determined by the width of the menu item. I am in bold in the currently selected item.

The problem is that when you shift the word becomes a little wider, which leads to the fact that the rest of the elements are slightly shifted to the left or right. Is there a smart way to prevent this? Something like lines indicating a residence permit to ignore the extra width caused by courage? My first thought was to simply subtract a few pixels from the complement of the active element, but this number varies.

If possible, I would like to avoid setting a static width for each record, and then centering as opposed to the currently used add-on to make future changes to simple elements.

+90
css fonts menu
Apr 15 '11 at 10:18
source share
11 answers

I had the same problem, but I got a similar effect with a little compromise, instead I used a text shadow.

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

Here is a working example:

 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 example

+130
Feb 20 '13 at
source share

Best working solution using :: 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; } 

It adds an invisible pseudo-element with the width of bold text obtained by the title attribute.

The text-shadow solution looks unnatural on a Mac and does not use all the beauty that rendering text on a Mac provides .. :)

http://jsfiddle.net/85LbG/

Credit: stack overflow

+82
Sep 14 '15 at 17:48
source share

The most portable and visually pleasing solution is to use text-shadow . This revises and shows examples of Torgeir's answer using Alexxali and my own settings:

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

This puts tiny “shadows” in black (use the font name / color code instead of black if necessary) on both sides of each letter, using units that will scale correctly when rendering the font.

warning Warning: px values ​​support decimal values, but they will not look so great when changing the font size (for example, the user scales the view using Ctrl + + ). Use relative values ​​instead.

This answer uses fractions of ex units since they are scaled by the font.
In most browsers, by default * expect 1ex1ex and therefore 0.025ex0.1px .

Look at yourself:

 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> 

Hover over the drawn lines to see how they differ from standard text.

Change the zoom level of the browser ( Ctrl + + and Ctrl + - ) to see how they change.

For comparison, I added two other solutions: the trick with the letter spacing @cgTag , which doesn’t work very well, since it includes guessing the font width ranges, and the @ workaholic_gangster911 :: after trick , which leaves an uncomfortable extra space for expanding bold text not pushing adjacent text elements (I put the attribution after the bold text so you can see how it doesn't move).




In the future, we will have more variable fonts capable of changing the quality of font-variation-settings using font-variation-settings . Browser support is expanding (Chrome 63+, Firefox 62+), but it requires more than just standard fonts, and only a few existing fonts support it.

If you embed a variable font, you can use CSS as follows:

 /* Grade: Increase the typeface 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; } } 

The Mozilla Modifiable Font Guide has a live demo with a slider for playing with various ratings. Google's Introduction to Variable Fonts on the Internet has an animated GIF that demonstrates the switch between high rating and no rating:

animated Amstelvar Alpha font demo with toggling grade axis

+22
Sep 27 '17 at 16:00
source share

I found that most fonts are the same size when you adjust the letter spacing to 1px.

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

So far, this has changed the regular font, so that each letter has an extra pixel spacing. For the menu, the names are so short that this is not a problem.

+16
May 22 '14 at 22:29
source share

Unfortunately, the only way to avoid changing the width when the text is in bold is to determine the width of the list item, however, as you stated that this is manual, it takes a lot of time and does not scale.

The only thing I can think of is to use some javascript that calculates the width of the tab before it is bold, and then applies the width at the same time when the bold is required (either on hover or on click).

+3
Apr 16 '11 at 9:30 a.m.
source share

Use JavaScript to set a fixed width li based on unexpanded content, then bold to add style to the <a> tag (or add a range if <li> has no children).

+2
Apr 16 2018-11-11T00:
source share

For a more relevant answer, you can use -webkit-text-stroke-width :

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

This avoids any pseudo-elements (which is a plus for screen readers) and text shadows (which look dirty and can still create a small “jump” effect) or fixed width settings (which may be impractical).

It also allows you to set the element to be bold than 1px (theoretically, you can make the font as bold as you like, and it can also be a workout to create a bold version of a font that doesn't have a bold font). option as custom fonts. Although this should be avoided, as some fonts may look fuzzy and uneven)

This definitely works in the most recent editions of Edge, Firefox, Chrome, and Opera. I assume this works in Safari (Opera is usually a good barometer), although I have not tested it in Safari. This does not work in IE11 or lower.

Also note that it uses the -webkit prefix, so it is not standard, and support may be discontinued in the future, so you should not rely on the fact that this is a bold font, it is really important - it is better to avoid this method, unless aesthetic.

+1
May 24 '18 at 14:19
source share

This is a very old question, but I am returning to it because I had this problem in the application under development, and I found all the answers here.

(Skip this paragraph for TL; DR ...) I use the Gotham web font from cloud.typography.com and I have buttons that start blank (with a white border / text and a transparent background) and get the background color on hover . I found that some of the background colors that I used did not contrast well with the white text, so I wanted to change the text to black for these buttons, but - whether due to a visual trick or common anti-aliasing methods - dark text on a light background always looks lighter than white text on a dark background. I found that increasing the weight from 400 to 500 for dark text supports almost the same “visual” weight. Nevertheless, it increased the width of the button by a tiny amount - a fraction of a pixel - but this was enough to make the buttons “tremble” slightly, which I wanted to get rid of.

Decision:

Obviously, this is a really difficult problem, so it required a complex solution. I ended up using negative letter-spacing in the text in a bold font, as recommended by cgTag above, but 1px would be superfluous, so I just calculated exactly the width that I would need.

Having studied the button in Chrome devtools, I found that the default width of my button is 165.47 pixels, and when hovering it is 165.69 pixels, that is, the difference is 0.22 pixels. The button had 9 characters, which means:

0.22 / 9 = 0.024444 pixels

By converting this to em units, I could make the font size setting independent. My button used a font size of 16 pixels, so:

0.024444 / 16 = 0.001527em

So for my particular font, the following CSS maintains the same button widths on hover:

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

Having tested a little and using the above formula, you can find the exact letter-spacing value for your situation, and it should work regardless of the font size.

The only caveat is that different browsers use slightly different subpixel calculations, so if you strive for this OCD level with perfect subpixel accuracy, you will need to repeat the test and set different values ​​for each browser. CSS styles targeting the browser are usually not approved , and there is a good reason for this, but I think this is the only use case when it is the only option that makes sense.

+1
01 Oct '18 at 18:17
source share

Interest Ask. I suppose you use float, right?

Well, I don’t know any technique that you can use to get rid of this font extension, so they will try to match the minimum required width - and changing the font thickness will change that value.

The only solution I know to avoid this change is the one you said you don't need: setting fixed sizes on li.

0
Apr 16 2018-11-11T00:
source share

You can implement this as amazon.com's "Shop by department" menu. It uses a wide div. You can create a wide div and hide its right side

0
Apr 04 '13 at 15:33
source share

UPDATE: you need to use the B tag for the title, because in IE11 the pseudo-class i: after did not display when I had the visibility: hidden.

In my case, I want to align the (custom) input checkbox / radio with the label text where the text is shown in bold when checking the input.

The solution presented here does not work for me in Chrome. Vertical alignment of input and labels messed up with: after class psuedo and -margins do not fix this.

Here is a fix where you will not get problems with vertical alignments.

 /* 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
Sep 29 '17 at 12:20
source share



All Articles