Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS dot leaders at first line over complex background

Tags:

html

css

flexbox

I need to implement dot leaders after first line of text using CSS to use it over texture/image background.

Expected behaviour:

enter image description here

I saw a few implementation of dot leaders over the internet. They all use one of the techniques below:

  1. ::after dots + overflow
<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;
}

  1. absolute dots + white background
<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;
}
  1. dumb flexbox
<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.

  1. Has dot leaders at last line.
  2. Does not work over complex background, even with mix-blend-mode (uncomment bg line to see). enter image description here
  3. Has large white gaps due to line breaks (resize to see). Switching to grid is no use. enter image description here

Is there more solutions to this case?

like image 701
panfil Avatar asked Nov 20 '21 01:11

panfil


People also ask

What if the text before the DOT LEADER is so long?

But what if the text before the dot leader is so long that it wraps over several lines? The row of dots has to be lower in that case, not behind the first line of the text, but behind the last. Without the proposed new features for CSS level 3 we cannot solve that in the general case, but we can still make it look reasonable in simple cases.

What is the difference between before and after leaders Li in CSS?

The CSS rules are the same as before, except that we now use the selector 'ul.leaders li:after' instead of 'ul.leaders li:before'. And there is one extra subtlety: because the dots are now logically after the last text, they will be drawn over, instead of under it.

Should the row of dots be lower behind the first line?

The row of dots has to be lower in that case, not behind the first line of the text, but behind the last. Without the proposed new features for CSS level 3 we cannot solve that in the general case, but we can still make it look reasonable in simple cases. We have to assume then that the text after the leader is short enough that it won't wrap.

What are the new CSS rules for dots after text?

The CSS rules are the same as before, except that we now use the selector 'ul.leaders li:after' instead of 'ul.leaders li:before'. And there is one extra subtlety: because the dots are now logically after the last text, they will be drawn over, instead of under it.


3 Answers

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>
Here 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>

Here I'm using white underline, instead of whole white background, to hide dotleader. But you'll get white underline while using some blend modes.

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>

Here instead of wrapping on word boundaries, I'm wrapping on letter boundaries using 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.
like image 50
the Hutt Avatar answered Sep 17 '22 12:09

the Hutt


Three improvements for the second technique

  1. 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.

  2. 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.

  3. And the gap property helps to set the minimum gap between the text and the price.

There are three demos in the snippet below:

  • First one shows mix-blend-mode: multiply in action.
  • The other two compare the effect of 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>
like image 25
Gleb Kemarsky Avatar answered Sep 18 '22 12:09

Gleb Kemarsky


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).

like image 44
PartialFlavor_55KP Avatar answered Sep 18 '22 12:09

PartialFlavor_55KP