0

I'm trying to copy this example; I have a scss file that I'm using with modular CSS in a React/Electron project. I want to define the function to be used by paint in the same file, as in the example:

.container {
  --background-canvas: (ctx, geom) => {
    // left blank for minimal example
  };
  background-image: paint(background-canvas);
  display: flex;
  margin: 4px;
  border-radius: 12px;
  height: 75px;
}

However, this fails to compile with CssSyntax error: Expected a pseudo-class or pseudo-element. (2:23). What am I not doing that the demo is?

IronWaffleMan
  • 2,513
  • 5
  • 30
  • 59

1 Answers1

1

Alright, I got it mostly working. The only part that isn't working is the transition which I'm not sure why it isn't.

-- Edit: I got that working via: https://css-houdini.rocks/animating-gradient

  CSS.registerProperty({
    name: '--multiplier',
    syntax: '<number>',
    inherits: false,
    initialValue: 0
  })

I couldn't find a way to get the CSS in JS parser to treat { } as a part of a string rather than special characters, so I used an array to allow me to run the relevant function calls in the background-canvas function.

  --background-canvas: (ctx, geom) => [ ctx.moveTo(0, 0),
    ctx.lineTo(
      var(--pad) + (geom.width - var(--slant) - var(--pad)) * var(--multiplier),
      0
    ),
    ctx.lineTo(
      var(--pad) + (geom.width - var(--slant) - var(--pad)) * var(--multiplier) +
        var(--slant),
      geom.height
    ),
    ctx.lineTo(0, geom.height), ctx.fillStyle = \`var(--color)\`, ctx.fill() ];

The real fun part about this solution is that you still need to actually register the paint function.

I did that in a similar way as a previous answer I have: https://stackoverflow.com/a/61966697/13175138 which uses this https://twitter.com/DasSurma/status/983305990731894785

As a note, this solution from that example uses eval as part of it's registerPaint function, so this could be problematic from a security standpoint, though the paint code should theoretically be sandboxed from the main runtime.

const Demo = styled.div`
  background: #1108a0;
  padding: 50px 0;
`;
const Test = styled.div`
  --color: cyan;
  --multiplier: 0.24;
  --pad: 30;
  --slant: 20;
  --background-canvas: (ctx, geom) => [ ctx.moveTo(0, 0),
    ctx.lineTo(
      var(--pad) + (geom.width - var(--slant) - var(--pad)) * var(--multiplier),
      0
    ),
    ctx.lineTo(
      var(--pad) + (geom.width - var(--slant) - var(--pad)) * var(--multiplier) +
        var(--slant),
      geom.height
    ),
    ctx.lineTo(0, geom.height), ctx.fillStyle = \`var(--color)\`, ctx.fill() ];
  background: paint(background-canvas);
  transition: --multiplier 0.4s;
  font: bold 6em sans-serif;
  color: yellow;
  text-shadow: 0 3px 1px cyan;
  line-height: 1.5em;
  width: max-content;
  padding-left: 30px;
  padding-right: 50px;
  isolation: isolate;
  &:hover {
    --multiplier: 1;
  }
  & span {
    mix-blend-mode: exclusion;
  }
`;
const App = () => (
  <Demo>
    <Test className="el" right={'right'}>
      <span>JS-in-CSS</span>
    </Test>
  </Demo>
);
ReactDOM.render(<App />, document.getElementById('root'));

/*if ("paintWorklet" in CSS) {
  console.log('here')
  const src = document.querySelector('script[language$="paint"]').innerHTML;
  const blob = new Blob([src], {
    type: 'text/javascript'
  });
  CSS.paintWorklet.addModule(URL.createObjectURL(blob));
}*/
@media (max-width: 900px) {
  .el {
    font-size: 4em;
  }
}
@media (max-width: 500px) {
  .el {
    font-size: 2.4em;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react-is@17.0.1/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id="root"/>

<script language="javascript+paint">
  registerPaint('background-canvas', class {
  static get inputProperties() {
      return ['--background-canvas'];
  }
  paint(ctx, geom, properties) {
      eval(properties.get('--background-canvas').toString())(ctx, geom, properties);
  }
})
</script>

<script>
  // Register the property so it become animatable
  CSS.registerProperty({
    name: '--multiplier',
    syntax: '<number>',
    inherits: false,
    initialValue: 0
  })
  if ("paintWorklet" in CSS) {
  const src = document.querySelector('script[language$="paint"]').innerHTML;
  const blob = new Blob([src], {
    type: 'text/javascript'
  });
  CSS.paintWorklet.addModule(URL.createObjectURL(blob));
}
</script>
Zachary Haber
  • 10,376
  • 1
  • 17
  • 31
  • I'm actually now trying to register the paint function in a different file (having a huge inline stringified function isn't the best for editability), following this page: https://blog.logrocket.com/how-to-create-randomly-generated-backgrounds-with-the-css-paint-api/, and I'm having issues getting it to register... In my `index.html` I have a ` – IronWaffleMan Nov 08 '20 at 19:08
  • You'd probably have better luck inlining a style tag in the index.html file as I have the registerPaint tag set up in these examples, or put your testPaint in the public folder and use it from there. It won't work if it gets bundled into the main file, I believe. – Zachary Haber Nov 09 '20 at 00:13
  • I posted a longer explanation in a new question here: https://stackoverflow.com/questions/64744884/cant-import-css-houdini-paint-js-file – IronWaffleMan Nov 09 '20 at 01:46
  • @IronWaffleMan, if this answer sufficiently answers this question, can you accept it? – Zachary Haber Nov 10 '20 at 02:34