51

Example use (what I want)

div::after {
  content: var(--mouse-x) ' / ' var(--mouse-y);
}

Test case showing it NOT working:

CodePen: CSS Variables in Pseudo Element's "content:" Property (a test case) by Jase Smith

document.addEventListener('mousemove', (e) => {
  document.documentElement.style.setProperty('--mouse-x', e.clientX)
  document.documentElement.style.setProperty('--mouse-y', e.clientY)
  
  // output for explanation text
  document.querySelector('.x').innerHTML = e.clientX
  document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
  content: var(--mouse-x, 245)" / " var(--mouse-y, 327);
}

/* setup and presentation styles */
div::before {
  content: 'mouse position:';
}
div {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
  width: 10em;
  height: 10em;
  background: #ff3b80;
  color: #fff;
  display: flex;
  flex-flow: column;
  align-items: center;
  justify-content: center;
  border-radius: 100%;
  will-change: transform;
}
body {
  margin: 2em;
  font-family: sans-serif;
}
p {
  max-width: 50%;
  min-width: 25em;
}
<!-- test case: element with pseudo element -->
<div></div>

<!-- explanation (not test case) -->
<main>
  <pre><code>div::after {
  content: var(--mouse-x) ' / ' var(--mouse-y);
}</code></pre>
  <h1>If this worked...</h1>
  <p>
    We should see something like this: <b><span class="x">245</span> / <span class="y">327</span></b> updating with the mousemove coordinates inside the pseudo <i>::after</i> element for the div.
  </p>
</main>
vsync
  • 118,978
  • 58
  • 307
  • 400
Jase
  • 603
  • 1
  • 6
  • 7
  • 2
    I'd suggest posting the code directly in the question in case the link breaks in the future. – darrylyeo Oct 20 '16 at 20:48
  • Ah, thanks for the tip. I was hoping it would just embed and show the CodePen demo... – Jase Oct 20 '16 at 22:07
  • This is a duplicate of http://stackoverflow.com/questions/38751778/is-it-possible-to-use-css-custom-properties-in-values-for-the-content-property but since you're dealing with numbers here, darrylyeo's hack will do just fine. – BoltClock Oct 21 '16 at 15:33

4 Answers4

75

Edit for clarity: CSS custom properties with integer values can be displayed in a pseudo-element's content property via a CSS counter.

div {
    --variable: 123;
}
span:after {
    counter-reset: variable var(--variable);
    content: counter(variable);
}
<div>The variable is <span></span>.</div>

.coordinates:before {
    counter-reset: x var(--x) y var(--y);
    content: 'The coordinates are (' counter(x) ', ' counter(y) ').';
}
<div class="coordinates" style="--x: 1; --y: 2"></div>

Original Answer

Got it to work using a hack involving CSS Counters. Enjoy.

div::after {
  counter-reset: mouse-x var(--mouse-x, 245) mouse-y var(--mouse-y, 245);
  content: counter(mouse-x) " / " counter(mouse-y);
}

Full code in action:

document.addEventListener('mousemove', (e) => {
  document.documentElement.style.setProperty('--mouse-x', e.clientX)
  document.documentElement.style.setProperty('--mouse-y', e.clientY)
  
  // output for explanation text
  document.querySelector('.x').innerHTML = e.clientX
  document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
  counter-reset: mouse-x var(--mouse-x, 245) mouse-y var(--mouse-y, 245);
  content: counter(mouse-x) " / " counter(mouse-y);
}

/* setup and presentation styles */
div::before {
  content: 'mouse position:';
}
div {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
  width: 10em;
  height: 10em;
  background: #ff3b80;
  color: #fff;
  display: flex;
  flex-flow: column;
  align-items: center;
  justify-content: center;
  border-radius: 100%;
  will-change: transform;
}
body {
  margin: 2em;
  font-family: sans-serif;
}
p {
  max-width: 50%;
  min-width: 25em;
}
<!-- test case: element with pseudo element -->
<div></div>

<!-- explanation (not test case) -->
<main>
  <pre><code>div::after {
  content: var(--mouse-x) ' / ' var(--mouse-y);
}</code></pre>
  <h1>If this worked...</h1>
  <p>
    We should see something like this: <b><span class="x">245</span> / <span class="y">327</span></b> updating with the mousemove coordinates inside the pseudo <i>::after</i> element for the div.
  </p>
</main>
Dai
  • 141,631
  • 28
  • 261
  • 374
darrylyeo
  • 3,103
  • 20
  • 30
7

content property only allows Strings, and since you are dealign with numbers and CSS cannot cast variables, you are left with the option to create another set of variables (from JS) which will serve as the printed values, and will be of type String.

To set --mouse-x-text as String, it's not enough to cast it to that type using the old casting trick 2+"" = "2", but JSON.stringify is the only way what I know that can output a "real" string, out of the already-string value, which kind-of mean a string of a string, since CSS seems to strip the first string-layer.

document.addEventListener('mousemove', ({clientX:x, clientY:y}) => {
  const {style} = document.documentElement

  style.setProperty('--mouse-x', x)
  style.setProperty('--mouse-y', y)

  // for printing
  style.setProperty('--mouse-x-text', JSON.stringify(x+""))
  style.setProperty('--mouse-y-text', JSON.stringify(y+""))
})
body::before{
  content: "X:"var(--mouse-x-text)"  Y:"var(--mouse-y-text);
}
vsync
  • 118,978
  • 58
  • 307
  • 400
  • 1
    Ohhh that `JSON.stringify` trick. I was struggling since hours. One more tip you can create string like `' " '+string+' " '` – santoshe61 Aug 03 '23 at 18:41
6

I'm not quite sure if I understood your question correctly, but I think here's a solution...

You can define a custom attribute to your <div> element.

<div data-position></div>

Then assign the position in this attribute with javascript:

  var position = e.clientX + " " + e.clientY
  document.querySelector("div").setAttribute('data-position', position)

Finally use the attr() CSS function in the content property of your pseudoelement.

div::after {
  content: attr(data-position);
}

And voila.


Code Snippet:

document.addEventListener('mousemove', (e) => {
  document.documentElement.style.setProperty('--mouse-x', e.clientX)
  document.documentElement.style.setProperty('--mouse-y', e.clientY)
  var position = e.clientX + "/" + e.clientY
  document.querySelector("div").setAttribute('data-position', position)
    // output for explanation text
  document.querySelector('.x').innerHTML = e.clientX
  document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */

div::after {
  content: attr(data-position);
}
/* setup and presentation styles */

div::before {
  content: 'mouse position:';
}
div {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
  width: 10em;
  height: 10em;
  background: #ff3b80;
  color: #fff;
  display: flex;
  flex-flow: column;
  align-items: center;
  justify-content: center;
  border-radius: 100%;
  will-change: transform;
}
body {
  margin: 2em;
  font-family: sans-serif;
}
p {
  max-width: 50%;
  min-width: 25em;
}
<div data-position></div>
<span class="x"></span>/<span class="y"></span>
Ricky Ruiz
  • 25,455
  • 6
  • 44
  • 53
  • 1
    Thanks @Ricky_Ruiz. Apologies for not being clearer with my post. While your solution produces a functional result, I'm more interested in using the CSS custom properties capabilities and not resorting to DOM manipulations on elements (and their attributes) directly. – Jase Oct 21 '16 at 06:48
  • @Jase I will give it another shot tomorrow morning to see if I can provide an answer that suits what you're looking for. Hopefully this can be solved the way you want to. Will keep you updated. Cheers. – Ricky Ruiz Oct 21 '16 at 06:58
  • @Jase Check DarryLeo's [**answer**](http://stackoverflow.com/a/40179718/4305494). – Ricky Ruiz Oct 21 '16 at 15:46
2

You need quotation marks around the values of your custom properties.

document.documentElement.style.setProperty('--mouse-x', "'" + e.clientX + "'")
document.documentElement.style.setProperty('--mouse-y', "'" + e.clientY + "'")
darrylyeo
  • 3,103
  • 20
  • 30
  • This does however seem to break the positioning where it's used as a int, I guess CSS doesn't do the type conversion for you like JS. – DBS Oct 20 '16 at 20:53
  • 1
    This is very close to what I was wanting. However, while this provides live updating of the values in the pseudo-element based purely on the CSS variables, it's a bummer the DIV lost its positioning capabilities, as @DBS pointed out. – Jase Oct 21 '16 at 06:52
  • 1
    @Jase Obviously it's not ideal, but you could just store an int and a string version of the same values (e.g. "--mouse-x" for int and "--mouse-x-s" for string) and use the relevant data type where needed. – DBS Oct 21 '16 at 08:40
  • 1
    Agreed, there's no way to do this with one set of variables as of now. – darrylyeo Oct 21 '16 at 14:21