I need to implement dot leaders after first line of text using CSS to use it over texture/image background.
Expected behaviour:
I saw a few implementation of dot leaders over the internet. They all use one of the techniques below:
<div class=item1>
<span class=text1>
Text
</span>
<span class=price1>
$100
</span>
</div>
.item1 {
display: flex;
}
.text1 {
flex-grow: 1;
position: relative;
overflow: hidden;
}
.text1::after {
content: '';
position: absolute;
bottom: 0;
border-bottom: 1px dotted grey;
width: 100%;
}
.price1 {
align-self: flex-end;
}
<div class=item2>
<span class=text2>
<span class=bg2>
Text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
.item2 {
display: flex;
justify-content: space-between;
position: relative;
mix-blend-mode: darken;
}
.item2::before {
content: '';
position: absolute;
top: 1em;
left: 0;
right: 0;
border-bottom: 1px grey dotted;
}
.bg2 {
background: white;
position: relative;
z-index: 1;
}
<div class=item3>
<span class=text3>
Text
</span>
<span class=dots3></span>
<span class=price3>
$100
</span>
</div>
.item3 {
display: flex;
align-items: flex-start;
}
.dots3 {
flex-grow: 1;
border-bottom: 1px dotted grey;
min-width: 10px;
height: 1em;
}
They are all shown here: https://jsfiddle.net/rzmLg4yu/62/
Each of these techniques has its own pitfalls in my case.


Is there more solutions to this case?
Technic 3 can be improved to get rid of the wrapping gaps:
body {
color: grey;
}
.width {
width: 300px;
padding: 5px;
resize: horizontal;
overflow: hidden;
abackground: linear-gradient(45deg, yellow, black);
background-repeat: repeat-x;
background-size: 200px 100%;
}
.item3 {
display: flex;
align-items: flex-start;
}
.dots3 {
flex-grow: 1;
border-bottom: 1px dotted grey;
min-width: 10px;
height: 1em;
}
.text3 {
padding-right: 0px;
text-align: justify; /* <--- */
}
<div class=width>
<div class=item3>
<span class=text3>
3 Text text
</span>
<span class=dots3></span>
<span class=price3>
$100
</span>
</div>
<div class=item3>
<span class=text3>3 Text text text text text text text text text text text text text text text text</span>
<span class=dots3></span>
<span class=price3>
$100
</span>
</div>
text-align: justify; will space the words evenly.
I think your issue with the second technic is because of white background color right?
because of .bg2 {background: white;...}?
Otherwise, you should've used mix-blend-mode: lighten; to demonstrate the issue:
body {
color: grey;
padding: 20px;
}
.width {
width: 230px;
padding: 5px;
resize: horizontal;
overflow: auto;
background: linear-gradient(90deg, yellow, black);
background-repeat: repeat-x;
background-size: 200px 100%;
}
div>div {
background-color: white;
border: 2px solid green;
}
.simpleDiv1 {
mix-blend-mode: darken;
}
.simpleDiv2 {
margin-top: 20px;
mix-blend-mode: lighten;
}
<div class=width>
<div class=simpleDiv1>darken blend. Simple text without any styles and stuff.</div>
<div class=simpleDiv2>lighten blend. Simple text without any styles and stuff.</div>
</div>
If you use darken blend then any normal text will disappear with that background, let alone dot leaders.
Technic two without background white:
body {
color: grey;
padding: 20px;
}
.width {
width: 230px;
padding: 5px;
resize: horizontal;
overflow: hidden;
abackground: linear-gradient(90deg, yellow, black);
background-repeat: repeat-x;
background-size: 200px 100%;
}
.item2 {
display: flex;
justify-content: space-between;
position: relative;
mix-blend-mode: darken;
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-color: white;
text-decoration-thickness: 2px;
}
.item2::before {
content: '\00a0';
height: calc(1em - 0.5px);
position: absolute;
top: 0;
left: 0;
right: 0;
border-bottom: 1px dotted gray;
z-index: -1;
}
<div class=width>
<div class=item2>
<span class=text2>
2 Text text
</span>
<span class=price2>
$100
</span>
</div>
<div class=item2>
<span class=text2>
2 Text text text text text text text text text text text text text text text text
</span>
<span class=price2>
$100
</span>
</div>
</div>
Technic three can be improved if we are willing to break words while wrapping:
body {
color: grey;
}
.width {
width: 300px;
padding: 5px;
resize: horizontal;
overflow: hidden;
abackground: linear-gradient(45deg, yellow, black);
background-repeat: repeat-x;
background-size: 200px 100%;
}
.item3 {
display: flex;
align-items: flex-start;
}
.dots3 {
flex-grow: 1;
border-bottom: 1px dotted grey;
min-width: 10px;
height: 1em;
}
.text3 {
padding-right: 0px;
word-break: break-all;
}
<div class=width>
<div class=item3>
<span class=text3>
3 Text text
</span>
<span class=dots3></span>
<span class=price3>
$100
</span>
</div>
<div class=item3>
<span class=text3>3 Text text text text text text text text text text text text text text text text</span>
<span class=dots3></span>
<span class=price3>
$100
</span>
</div>
word-break: break-all. If your font-size is 1em, then this way the maximum gap will be less than 1em, because browser won't be able to fit a letter less than 1em space.
Even the W3C official source has some disparity. You may see inconsistent rendering or poor support for invented features (such as non-monospace/preformatted, lead zero padding, varied currency widths).
Using a shim or Web Component such as Leader-Line would be advisable. I would recommend you experiment with dash-animation to increase visibility on busy backgrounds (at the expense of annoying someone with a busy leader).
I suggest using the value multiply for the mix-blend-mode property. In the case of dark text, it gives a sharper result even on a gradient background. Look the first demo.
We can simplify layout of the second technique with a help of the ::first-line pseudo-element. It can be used with "only a small subset of CSS properties", but this subset contains all background-related properties.
I think this also gives a better result against mix-blend-mode: darken. Compare the second and third demo.
And the gap property helps to set the minimum gap between the text and the price.
There are three demos in the snippet below:
mix-blend-mode: multiply in action.mix-blend-mode: darken on two layouts - with an extra bg block and with the ::first-line pseudo-element.https://codepen.io/glebkema/pen/NWapXPQ?editors=1100
body {
color: grey;
}
.width {
width: 300px;
padding: 5px;
resize: horizontal;
overflow: hidden;
background: linear-gradient(45deg, yellow, black);
background-repeat: repeat-x;
background-size: 200px 100%;
}
.item2 {
display: flex;
justify-content: space-between;
position: relative;
mix-blend-mode: darken;
gap: 10px; /* 3) */
}
.multiply .item2 {
mix-blend-mode: multiply; /* 1) */
}
.item2::before {
content: '';
position: absolute;
top: 1em;
left: 0;
right: 0;
border-bottom: 1px grey dotted;
}
.bg2 {
background: white;
position: relative;
z-index: 1;
}
.price4,
.text4 {
z-index: 1;
}
.price4,
.text4::first-line { /* 2) */
background: white;
}
<h4><code>mix-blend-mode: multiply;</code></h4>
<div class="width multiply">
<div class=item2>
<span class=text2>
<span class=bg2>
2 Text text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
<div class=item2>
<span class=text2>
<span class=bg2>
2 Text text text text text text text text text text text text text text text text text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
<br />
<div class=item2>
<span class=text4>
4 Text text
</span>
<span class=price4>
$100
</span>
</div>
<div class=item2>
<span class=text4>
4 Text text text text text text text text text text text text text text text text text
</span>
<span class=price4>
$100
</span>
</div>
</div>
<h4><code>mix-blend-mode: darken;</code> (2nd method before 4th)</h4>
<div class="width">
<div class=item2>
<span class=text2>
<span class=bg2>
2 Text text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
<div class=item2>
<span class=text2>
<span class=bg2>
2 Text text text text text text text text text text text text text text text text text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
<br />
<div class=item2>
<span class=text4>
4 Text text
</span>
<span class=price4>
$100
</span>
</div>
<div class=item2>
<span class=text4>
4 Text text text text text text text text text text text text text text text text text
</span>
<span class=price4>
$100
</span>
</div>
</div>
<h4><code>mix-blend-mode: darken;</code> (4th method before 2nd)</h4>
<div class="width">
<div class=item2>
<span class=text4>
4 Text text
</span>
<span class=price4>
$100
</span>
</div>
<div class=item2>
<span class=text4>
4 Text text text text text text text text text text text text text text text text text
</span>
<span class=price4>
$100
</span>
</div>
<br />
<div class=item2>
<span class=text2>
<span class=bg2>
2 Text text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
<div class=item2>
<span class=text2>
<span class=bg2>
2 Text text text text text text text text text text text text text text text text text
</span>
</span>
<span class=price2>
<span class=bg2>
$100
</span>
</span>
</div>
</div>
You can modify technique 1 to move the dot functionality from the text to another span:
<div class=item1>
<span class=text1>
Text
</span>
<span class=line1></span>
<span class=price1>
$100
</span>
</div>
<div class=item1>
<span class=text1>
Text text text text text text text text text text text text
</span>
<span class=line1></span>
<span class=price1>
$100
</span>
</div>
<div class=item1>
<span class=text1>
Text text text text text text text text text text text text
</span>
</div>
.item1 {
display: flex;
}
.line1 {
flex-grow: 1;
position: relative;
overflow: hidden;
}
.line1::after {
content: '';
position: absolute;
bottom: 0;
border-bottom: 1px dotted grey;
width: 100%;
}
.price1 {
align-self: flex-end;
}
This works as intended:
And a JSFiddle
.item1 {
display: flex;
font-size:32px;
color:#808080;
}
.line1 {
flex-grow: 1;
position: relative;
overflow: hidden;
}
.line1::after {
content: '';
position: absolute;
bottom: 0;
border-bottom: 1px dotted #808080;
width: 100%;
}
.price1 {
align-self: flex-end;
}
<div class=item1>
<span class=text1>Text text</span>
<span class=line1></span>
<span class=price1>$100</span>
</div>
<div class=item1>
<span class=text1>Text text text text text text text text text</span>
<span class=line1></span>
<span class=price1>$100</span>
</div>
<div class=item1>
<span class=text1>Text text text text text text text</span>
</div>