Создайть нижнюю границу меньшего размера, чем элемент, и с более толстой серединой
Я использую стилизованные HR для создания разделителей секций.
Но теперь я пытаюсь создать элемент H1 с нижней границей меньше ширины H1 (переменная ширина в соответствии с шириной текста H1) и с более толстой чертой в середине (высотой).
Что-то вроде этого:
Я просмотрел несколько поисковых источников, пытаясь найти решение, но не нашел ничего похожего.
Я пытался использовать: after и: before, но все равно застрял. Есть идеи?
Вот что я пробовал до сих пор:
h1 {
display:inline;
border-bottom:1px solid black;
}
h1:after {
content: '';
display: block;
width: 100%;
height: 100%;
border-bottom:3px solid red;
}
<h1>
My Text
</h1>
Свободный перевод вопроса Create border-bottom smaller than element and with thicker middle от участника @Russel Riehle.
Ответы (5 шт):
Вы можете легко сделать это с помощью linear-gradient без дополнительной разметки или псевдоэлемента:
h1 {
display:inline-block;
font-size:3em;
padding-bottom:10px;
background:
linear-gradient(red 0 0) 50% calc(100% - 2px)/80% 2px,
linear-gradient(red 0 0) 50% 100% /40% 6px;
background-repeat:no-repeat;
}
<h1>Lorem Ipsum</h1>
Свободный перевод ответа от участника @Temani Afif.
Нельзя назвать данный ответ правильным, но попытка использовать canvas для этой задачи. Заметьте, что свойства h1 меняются только на время вычисления истинной ширины текста, затем возвращается все на место. H1 сохраняет свойство display:block, а также текст расположен по центру. При этом подчеркивание только на ширину самого текста.
Для того, чтобы поведение canvas было "естественным", добавляю свойство display:inline. Так он будет отцентрирован как и остальной текст, но требуется искусственный перенос на следующую строку. И эту проблему я смог решить только с помощью добавления тега <br>. И вот никак не могу уменьшить его высоту чтобы подчеркивание прижалось к тексту.
const LINE_COLOR = '#ea596e';
let h1 = document.querySelector('h1');
let canvas = document.querySelector('canvas');
canvas.width = 0;
canvas.height = 0;
let display = window.getComputedStyle(h1).getPropertyValue("display");
let align = window.getComputedStyle(h1).getPropertyValue("text-align");
h1.style.textAlign = 'left';
h1.style.display = 'inline-block';
let rect = h1.getBoundingClientRect();
h1.style.textAlign = align;
h1.style.display = display;
let w = canvas.width = rect.width;
let h = canvas.height = 5;
let ctx = canvas.getContext('2d');
ctx.fillStyle = LINE_COLOR;
ctx.fillRect(20,2,w-40,1);
ctx.fillRect(w/4,0,w/2,h);
<h1 style="text-align:center">What an awsome solution!
<br>
<canvas style="display:inline"></canvas>
</h1>
Добавляю и "улучшенный" вариант с текстом, заключенным в span. Вместо br использую div с отрицательным margin-top. Теперь не надо играть свойствами h1 чтобы получить ширину текста. Код проще.
Свойство display:inline для canvas, похоже смысла не имело, убираю лишнее.
const LINE_COLOR = '#ea596e';
let h1_span = document.querySelector('h1 > span');
let canvas = document.querySelector('canvas');
let w = canvas.width = h1_span.getBoundingClientRect().width;
let h = canvas.height = 5;
let ctx = canvas.getContext('2d');
ctx.fillStyle = LINE_COLOR;
ctx.fillRect(w/8,2,w-w/4,1);
ctx.fillRect(w/4,0,w/2,h);
<h1 style="text-align:center">
<span>What an awsome solution!</span>
<div style="margin-top: -16px"></div>
<canvas></canvas>
</h1>
Вариант с псевдоэлементами
h1 {
display: inline-block;
position: relative;
}
h1::before{
content: '';
position: absolute; bottom: 2px; left: 50%;
width: calc(100% - 2rem);
height: 1px;
background-color: red;
transform: translateX(-50%);
}
h1::after {
content: '';
display: block;
width: 50px;
height: 100%;
margin: 0 auto;
border-bottom: 5px solid red;
}
<h1>My Text</h1>
<br>
<h1>My Text Text Text Text Text Text Text Text</h1>
С использованием SVG (насколько хватает моих скудных познаний в данной технологии):
h1 {
width: fit-content;
margin: 0.5em auto;
font: bold 3rem sans-serif;
}
.test {
display: block;
width: 50%; height: 3px;
margin: 0.1em auto 0;
transition: 0.3s ease;
}
.test .line { stroke: red; fill: red; transition: inherit; }
#grow:checked ~ h1 .test { width: 100%; }
#proportions:checked ~ h1 .test .line.thick { transform: scaleX(0.25); }
#thickness:checked ~ h1 .test { height: 6px; }
<input id="grow" type="checkbox"><label for="grow">Увеличить ширину</label><br>
<input id="proportions" type="checkbox"><label for="proportions">Изменить пропорции сегментов</label><br>
<input id="thickness" type="checkbox"><label for="thickness">Повысить толщину</label><br>
<h1>Lorem Ipsum
<svg class="test" viewBox="0 0 100 3" preserveAspectRatio="none">
<line class="line thin" x1="0" y1="50%" x2="100" y2="50%" stroke="black" stroke-width="1px" />
<rect class="line thick" width="100%" height="100%" transform="scale(0.5 1)" transform-origin="center" />
</svg>
</h1>
Больше всего трудностей - с центровкой элемента rect'а (толстая часть линии) при сохранении возможности изменять его ширину.
При том что transform в SVG не работает с процентами (что похоже исключает динамическую центровку, а значит и задание ширины в абс. единицах таких как px), получилось сделать изменение размера толстого сегмента только относительно, через scale: т.е. в диапазоне 0..1, где 1 это полная ширина всей линии.
Решение с помощью псевдоэлемента и background-image:
h1 {
display: inline-block;
}
h1:after {
content: '';
display: block;
background-image: url("data:image/gif;base64,R0lGODlhCgAFAJEAAP8AAP///wAAAAAAACH5BAkAAAEALAAAAAAKAAUAAAgYAAMIBEAQgMCDAQoaHKiw4UGFCBkSPBgQAA==");
background-repeat: no-repeat;
background-size: 100% 5px;
height: 5px;
image-rendering: pixelated;
margin-top: 0.5em;
margin-left: 20%;
margin-right: 20%;
}
<h1>Very small!</h1><br>
<h1>This is small header!</h1><br>
<h1>This very very very long header!</h1>
Проблемы все те же + изображение настраивать. Отступы можно задавать при помощи margin-left, margin-right.
