CSS: жирність деякого тексту без зміни розміру контейнера


119

У мене є горизонтальне навігаційне меню, яке в основному просто <ul>з елементами, встановленими поруч. Я не визначаю ширину, а просто використовую набивання, тому що я хотів би, щоб ширини визначалися шириною пункту меню. Я напівжирним обраним на даний момент предметом.

Проблема полягає в тому, що в жирному шрифті слово стає дещо ширшим, через що решта елементів трохи зміщуються вліво або вправо. Чи є розумний спосіб запобігти цьому? Щось уздовж рядків, що розповідають прокладці, щоб ігнорувати додаткову ширину, спричинену шліфуванням? Моя перша думка полягала в тому, щоб просто відняти кілька пікселів від прокладки "активного" елемента, але ця сума змінюється.

Якщо можливо, я хотів би уникати встановлення статичної ширини для кожного запису, а потім центрування на відміну від рішення прокладки, яке я маю на даний момент, щоб зробити майбутні зміни записів простими.


чому проблема зміни розміру? Це чи-небудь псує макет?
Chaulky

Я думаю, що питання веб-програмування найкраще задавати на Stack Overflow. Можливо, мод перенесе це туди.
Ciaran

2
Chaulky: тому що я ненавиджу кілька речей, які я більше ненавиджу, ніж тоді, коли кнопки, які ти намагатимешся клацнути, перескакують
Mala

doejo.com/blog/… - це одне рішення, яке я знайшов, що робить саме те, що Джон і Blowski говорили про jscript.
thebulfrog

Відповіді:


152

У мене була така ж проблема, але я отримав подібний ефект з невеликим компромісом, і я замість цього використовував text-shadow.

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. Виглядає трохи сміливіше.
М. Герольд

Мені потрібно було лише рішення css, і це відповідь.
JCasso

ваш справжній геній! простота зробить її приголомшливою!
lufi

1
Лише додавання li:hover {text-shadow: 1px 0 0 black;}призвело до тексту з занадто маленьким проміжком між літерами. Щоб знову покращити це, ми також встановилиli {letter-spacing: 1px;}
Патрік Хаммер

5
Я використовував -0.5px 0 #fff, 0.5px 0 #fff, тому що ми могли бачити невеликий проміжок між самим текстом і тінню. А також, коли текст є напівжирним, він додає ваги обом сторонам.
atorscho

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 атрибутом.

The text-shadowРішення виглядає неприродно на Mac і не використовує всю красу , що текст рендеринга на Mac пропозицій .. :)

http://jsfiddle.net/85LbG/

Кредит: https://stackoverflow.com/a/20249560/5061744


11
Це найнадійніше і найчистіше рішення, воно просто залишає місце для жирного тексту в елементі. У моєму випадку додатковий псевдоелемент спричинив зміну висоти. Додавання негативу margin-topдо ::afterблоку вирішило це.
Вацлав Новотний

2
Це рішення чудове! Я використовував JS, щоб додати атрибут "data-content", який містить текст елемента, щоб я міг уникнути зміни HTML.
mistykristie

1
Дивовижно. Це має бути найкращою відповіддю. Зауважте, що якщо інший елемент, ніж li, можливо, вам доведеться використовувати display: block; на батьківському елементі:.
Blackbam

Чудове рішення! Добре працює і з: раніше, але в моєму випадку це запобігає додатковій маржі внизу.
Томас Чемпіон

Дякую за таке рішення. Спочатку я використовував text-shadow, але це не працює в Safari, оскільки він округляє десяткові значення. Оскільки я використовував 0,1 пікселя, це було закруглено до 0. Ваше рішення працювало бездоганно. Я лише замінив атрибут заголовка на ім'я, оскільки не хотів з'являтися підказки
Джонатан Кардос

32

Найбільш портативним та візуально приємним рішенням буде використання text-shadow. Це переглядає та показує приклади відповіді Торгейра за допомогою " Алексалі" та моїх власних змін.

  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 :: після виведення трюку , що залишає незручне додаткове місце, щоб сміливий текст розширювався не торкаючись сусідніх текстових елементів (я ставлю атрибуцію після жирного тексту, щоб ви могли бачити, як вона не рухається).


В майбутньому ми будемо мати більше змінних шрифтів, здатних до таких речей, як зміна шрифту via 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

Я встановив, що більшість шрифтів однакового розміру, коли ви регулюєте пробіли літер на 1 px.

a {
   letter-spacing: 1px;
}

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

Хоча це змінює звичайний шрифт, так що кожна літера має додатковий інтервал у пікселях. У меню назви такі короткі, що це не є проблемою.


3
це найкраще і найбільш занижене рішення, хоча я змінив його так, щоб він починався з 0 і переходив до від'ємного проміжку літер, коли виділяється жирним шрифтом. також вам доведеться тонко налаштувати його для кожного шрифту та розміру шрифту. Я також оновив загадку @ Thorgeir, щоб включити це (як 2-е рішення) jsfiddle.net/dJcPn/50/
robotik

Сучасні браузери тепер підтримують точність пікселів. Таким чином, ви можете точно настроїти інтервал за допомогою 0.98pxдрібних фракцій.
Реакційний

Це виглядає дещо дивним, оскільки алгоритм інтервалу не зовсім однаковий (деякі букви трохи танцюють), і, що ще важливіше, це не працює, коли шрифти масштабуються, навіть якщо ви конвертуєте 1pxвідносні значення, як 0.025exабо 0.0125ex. Дивіться мою відповідь для демонстрації наживо.
Адам Кац

@AdamKatz Я підозрюю, що візуалізація шрифту змінилася після опублікування цієї відповіді, і це дуже залежить від того, який шрифт ви використовуєте.
Реакційний

5

Для отримання більш актуальної відповіді ви можете скористатися -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

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

Це дозволяє уникнути будь-яких псевдоелементів (що є плюсом для читачів екрану) та текстових тіней (що виглядає безладним і все ще може створити невеликий ефект «стрибка») або встановлення будь-якої фіксованої ширини (що може бути непрактично).

Це також дозволяє встановити елемент, який має бути сміливішим, ніж 1 пікс (теоретично, ви можете зробити шрифт настільки жирним, як вам подобається, а також можна зробити трепетну тренування для створення жирної версії шрифту, що не має жирного шрифту. варіант, як-от користувацькі шрифти ( редагувати: змінні шрифти знецінюють цю пропозицію ). Хоча цього слід уникати, оскільки, ймовірно, деякі шрифти виглядатимуть непоказними та нерівними)

Я, безумовно, працює в Edge, Firefox, Chrome і Opera (на момент публікації) та в Safari ( редагувати: @ Lars Blumberg спасибі за підтвердження цього ). Він НЕ працює в IE11 або нижче.

Також зауважте, що він використовує -webkitпрефікс, тому це не є стандартним, і підтримка може знизитися в майбутньому, тому не покладайтеся на це сміливо, це дійсно важливо - краще уникати цієї техніки, якщо це не просто естетична.


1
Також працює на Safari 13.0.5. Найчистіше для мене рішення поки що.
Ларс Блюмберг

4

На жаль, єдиний спосіб уникнути зміни ширини, коли текст виділяється жирним шрифтом, - це визначити ширину елемента списку, однак, як ви заявляли, що це робиться вручну, це трудомістко і не масштабується.

Єдине, про що я можу придумати, - це використовувати javascript, який обчислює ширину вкладки, перш ніж вона буде жирною, а потім застосовує ширину одночасно із жирним шрифтом, необхідним (будь-коли під наведенням чи натисканням).


хм, я буду експериментувати з цим, хоча я хочу, щоб він принаймні виглядав розумним (тобто напівжирним) для користувачів, які не є JS. Можливо, я можу надіслати його жирним шрифтом, використати JSto, щоб розв'язати його, обчислити ширину, а потім знову жирним шрифтом? Або це просто нерозумно?
Мала

Так, це звучить як найкращий спосіб розмістити користувачів, що не мають JavaScript.
ajcw

2

Використовуйте JavaScript, щоб встановити фіксовану ширину li на основі нежирного вмісту, а потім жирним змістом <a>додайте стиль до тегу (або додайте проміжок, якщо у <li>нього немає дітей).


На жаль, вибачте за повторення: $
Ден Удар

Дякую за вашу відповідь все одно :)
Мала

@Mala Ви знайшли рішення? Як не дивно, у мене є подібна проблема.
Ден Удар

1

Це дуже давнє запитання, але я його переглядаю, оскільки у мене була ця проблема в додатку, який я розробляю, і я знайшов усі відповіді тут бажаючими.

(Пропустіть цей абзац для TL; DR ...)Я використовую веб-шрифт Gotham з cloud.typography.com, і у мене є кнопки, які починаються порожними (з білою облямівкою / текстом і прозорим фоном) і набувають колір тла при наведенні курсору. Я виявив, що деякі кольори тла, які я використовував, не добре контрастували з білим текстом, тому я хотів змінити текст на чорний для цих кнопок, але - чи то через візуальний трюк чи звичайні методи згладжування - темний Текст на світлому тлі завжди здається легшим, ніж білий текст на темному тлі. Я виявив, що збільшення ваги з 400 до 500 для темного тексту підтримувало майже таку саму «візуальну» вагу. Однак це збільшувало ширину кнопки на невелику кількість - частку пікселя, - але цього було достатньо, щоб кнопки трохи «тремтіли», чого я хотів позбутися.

Рішення:

Очевидно, це справді хитра проблема, тому вона вимагала прискіпливого рішення. Зрештою, я використав мінус letter-spacingна сміливіший текст, як рекомендовано вище cgTag, але 1px був би надмірним, тому я просто обчислив саме ту ширину, яка мені знадобиться.

Оглянувши кнопку в Chrome devtools, я виявив, що ширина моєї кнопки за замовчуванням становила 165,47px, а 165,69px на наведення курсора, різниця 0,22px. На кнопці було 9 символів, отже:

0,22 / 9 = 0,024444px

Перетворивши це в em одиниці, я міг зробити регулювання розміру шрифту агностичним. Моя кнопка використовувала шрифт розміром 16 пікселів, так що:

0,024444 / 16 = 0,001527ем

Отже, для мого конкретного шрифту наступний 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 і приховати його правильну частину


0

ОНОВЛЕННЯ: Довелося використовувати тег B для заголовка, оскільки в IE11 псевдоклас i: after не показав, коли у мене була видимість: приховано.

У моєму випадку я хочу вирівняти прапорець / радіо вводу (призначений для користувача) з текстом мітки, де текст стає жирним під час перевірки введення.

Надане тут рішення не працює для Chrome. Вертикальне вирівнювання вводу та мітки заплуталось у: після класу 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>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.