5

I want to emphasise the last 3 characters of a string, example:

123456789

Easy to wrap the last three with <strong> or <span class="">, but I was wondering if it could be done with CSS only? So the html would be something like:

<span class="mytext">123456789</span>

With the emphasis added in CSS mytext class?

6
  • 2
    you can style first letter, but not a generic substring
    – cloned
    Commented Apr 10, 2020 at 8:01
  • Something simillar: stackoverflow.com/questions/23569441/… Commented Apr 10, 2020 at 8:01
  • 1
    No, it cannot be done without wrapping those characters in an additional element. Even in the answer given you have an additional wrapping element ::after.
    – connexo
    Commented Apr 10, 2020 at 8:13
  • Tell us what's the purpose of doing that, maybe we can help Commented Apr 10, 2020 at 8:32
  • @SaymoinSam it is to make the last three characters stand out, as they are what the user is usually trying to read.
    – Tony H
    Commented Apr 11, 2020 at 9:54

3 Answers 3

2

Here is a crazy idea using filter and SVG to simulate a bold effect on the last 3 digits.

Works only on chrome

.mytext {
  display: inline-block;
  margin: 10px;
  font-size: 50px;
  font-family: monospace;
  position: relative;
  filter: drop-shadow(0px 0px 0px black);
}

.mytext:after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 3ch;
  backdrop-filter: url(#shadow);
}
<span class="mytext">123456789</span>

<span class="mytext" style="font-size:25px">123456789568</span>
<span class="mytext" style="font-size:25px">123568</span>



<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
  <defs>
  <filter id="shadow" color-interpolation-filters="sRGB" x="-50%" y="-50%" height="200%" width="200%">
<!-- Take source alpha, offset it by angle/distance and blur it by size -->
<feOffset id="offset" in="SourceAlpha" dx="0" dy="0" result="SA-offset"/>
<feGaussianBlur id="blur" in="SA-offset" stdDeviation="1" result="SA-o-blur"/>
<!-- Apply a contour by using a color curve transform on the alpha and clipping the result to the input -->

<feComponentTransfer in="SA-o-blur" result="SA-o-b-contIN">
  <feFuncA id="contour" type="table" tableValues="0 1 0.65 0.5 0.55 0.6 0.65 0.55 0.4 0.1 0"/>
</feComponentTransfer>

<feComposite operator="in" in="SA-o-blur" in2="SA-o-b-contIN" result="SA-o-b-cont"/>

<!-- Adjust the spread by multiplying alpha by a constant factor --> <feComponentTransfer in="SA-o-b-cont" result="SA-o-b-c-sprd">
  <feFuncA id="spread-ctrl" type="linear" slope="11"/>
</feComponentTransfer>

<!-- Adjust color and opacity by adding fixed offsets and an opacity multiplier -->
<feColorMatrix id="recolor" in="SA-o-b-c-sprd" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="SA-o-b-c-s-recolor"/>

<!-- Generate a reasonably grainy noise input with baseFrequency between approx .5 to 2.0. And add the noise with k1 and k2 multipliers that sum to 1 -->
<feTurbulence result="fNoise" type="fractalNoise" numOctaves="6" baseFrequency="1.98"/>
<feColorMatrix in="fNoise" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 7 -3" result="clipNoise"/>
<feComposite id="noisemix" operator="arithmetic" in="SA-o-b-c-s-recolor" in2="clipNoise" k1="0" k2="1" result="SA-o-b-c-s-r-mix"/>

<!-- Merge the shadow with the original -->
<feMerge>
  <feMergeNode in="SA-o-b-c-s-r-mix"/>
  <feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
  </defs>
</svg>

enter image description here

I used the following link to generate the filter: https://codepen.io/mullany/pen/sJopz

2
  • It does what I asked for I will give you that haha.
    – Tony H
    Commented Apr 11, 2020 at 9:49
  • 1
    Look ok in latest edge, also. (which is not surprising)
    – vals
    Commented Apr 11, 2020 at 10:06
2

Just a crazy idea, that actually get a display a little bit wrong.

Just post it to give some ideas for someone else to improve it.

.mytext {
  display: inline-block;
  margin: 10px;
  font-size: 50px;
  font-family: monospace;
  position: relative;
  color: black;
  text-shadow: -1px -1px 0px white, -1px 0px 0px white,  -1px 1px 0px white, 
  0px -1px 0px white,   0px 0px 0px white, 0px 1px 0px white,   
  1px -1px 0px white, 1px 0px 0px white, 1px 1px 0px white;
}

.mytext:after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 3ch;
  background-color: white;
  mix-blend-mode: difference;
}
<span class="mytext">123456789</span>

<span class="mytext" style="font-size:25px">123456789568</span>
<span class="mytext" style="font-size:25px">123568</span>

1

::before and ::after Pseudo-elements { content: attr() } Values

Demo 1

  1. Use the pseudo-elements ::before and ::after
  2. Add data-before="0123456" and data-after="789" to the element
  3. Set the pseudo-elements with content: attr(data-before) and content: attr(data-after) and font-weight: 900

ch Unit of Measurement and Monospaced Fonts

Demo 2

  1. Set the following at the top of the CSS:

     :root, body { font: 100 3.5vw/0.75 Consolas, monospace; }
    

    All font related properties will reference this ruleset. Not only will this facilitate consistency, it is also very responsive.

  2. 1ch = the width of a zero character: "0" from the current font-family. If your font-family a monospace typeface then all of your characters are exactly the same width.


Demo 1

Note: There are two examples: one without text content and two with text content.

section {
  font: 400 16px/1.45 Consolas;
  background: gold;
  color: blue;
}

.count::before {
  content: attr(data-before);
}

.count::after {
  content: attr(data-after);
  font-weight: 900;
}

.B {
  font-size: 0;
}

.B::before,
.B::after {
  font-size: 1rem
}

.C::after {
  content: attr(data-mask);
  position: relative;
  left: -3ch;
  z-index: 1;
  font-weight: 900;
}
<section class='count A' data-before='0123456' data-after='789'></section>

<section class='count B' data-before='0123456' data-after='789'>0123456789</section>

<section class='count C' data-mask='789'>0123456789</section>


Demo 2

Note: This demo is a more complex version of the third example in Demo 1

:root,
body {
  font: 100 3.5vw/0.75 Consolas, monospace;
}

.box {
  border: 0;
  margin: 0 auto 50px;
  padding: 0;
  outline: 5px solid tomato;
  max-width: max-content;
  max-height: calc(3.5vw * .75);
  white-space: nowrap;
}

section {
  display: inline-block;
  position: relative;
  border: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
  max-width: max-content;
  white-space: nowrap;
  letter-spacing: 0.1ch;
  background: gold;
  color: navy;
}

.D {
  z-index: 1;
}

.E {
  z-index: 0;
  margin-left: -1ch;
  font-weight: 900;
  color: red;
}

.red50-3 {
  max-width: calc(50% - 3ch);
}

.mov50-3 {
  right: calc(50% - 3ch);
}
<article class='box'>
  <section class='D red50-3'>0123456789</section>
  <section class='E mov50-3'>0123456789</section>
</article>

4
  • As it would require work either in javascript or server side to make the split strings it will not give any benefit to me. But interesting all the same thank you.
    – Tony H
    Commented Apr 11, 2020 at 9:52
  • @TonyH Ur...eh? JavaScript?...Nope, no JavaScript...Server side?...Nope, purely client side. In fact, it's only static HTML and CSS. Keep in mind there are only a handful of techniques that can facilitate dynamic behavior using only CSS and HTML. Even so, my answer isn't even dynamic...everything is hard coded HTML/CSS.
    – zer00ne
    Commented Apr 12, 2020 at 0:20
  • I mean if you have the string 0123456789 in the database on server side, you will have to do something either server side or in JS to create the substrings 0123456 and 789. Trivial, but what I was looking to avoid.
    – Tony H
    Commented Apr 13, 2020 at 11:38
  • See Demo 2 the caveat is that a duplicate tag is needed. The JS needed to duplicate a tag and its text is: const x = document.querySelector(selector).outerHTML; document.body.innerHTML += x; and the classes would be arranged as well. Keep in mind you are asking for a way to handle primitive values via CSS...impossible, CSS is presentation not a programming language.
    – zer00ne
    Commented Apr 14, 2020 at 12:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.