Making JavaScript more CSS friendly.

Let's assume that JS is used mostly in context of Web (in browser or node/deno) so it would be nice for JS to allow to write CSS "directly" - so for JS syntax to include CSS syntax (at least partially) .

All below is based on Sciter's script that was designed to better (than JS) serve "language-behind- UI" role. Yet below ideas are 100% backward compatible - existing code will work as it is.

Proposal #1, syntax-only, object literals

In object literals allow ; to be used as , name/value pairs separators. So

{
   font: "system";
   opacity:0.5;
}

will be a valid object literal. As far as I know it will not break existing code.

Proposal #2, syntax-only, object literals

To use CSS's nmtoken production in field names. CSS's name token matches JS name token but also allows - inside names. So this

{
   font-family: "system";
   opacity:0.5;
}

will be a direct equivalent of

{
   "font-family": "system";
   opacity:0.5;
}

Proposal #3, syntax & runtime

To introduce Tuple type.

Tuple is a) tagged and b) immutable vector/array. Tuple literals look close to array literals :

[ nmtoken ':' element1, element2, ...]

Motivation: CSS uses "functions" in notation that are actually tuples - not functions.

So this CSS declaration:

{
   background-color: rgb(255,0,0) ;
}

Can be expressed in JS++ as

{
   background-color: [rgb:255,0,0];
}

or with some helper CSS namespace object as:

{
   background-color: CSS.rgb(255,0,0);
}

Tuple class is essentially Array class a) without mutating methods and b) with .tag:string property.

Proposal #4, syntax-only, numbers with units

For parser to support units, so this JS++ construct

{
   border-width: 0.5em;
}

will be translated to tuple as:

{
   border-width: [em:0.5];
}

FYI : I am running campaign to Open Source Sciter now.

Proposal one is a block with two labeled statements, is it not?

No.

  1. Labels cannot appear in object literals;
  2. When JS parser gets opening { it knows exactly is it block or object literal.

As much as I love CSS, I don't think it's a good idea to do this for several reasons.

One is, you probably don't want to tie one language to another. What if CSS gets new features? Would that require JS engines to update as well?

As far as syntax goes, I'm not convinced any of the proposed syntaxes are better than just a template literal. Especially for more complex values, like the following:

border-top: 23px solid rgb(0 40 123 / 40%);
background: no-repeat center / 80% url("../img/image.png");
content:
    "hello there, "
    attr(name)
    ", I see you've gotten to page "
    counter(page)
    ".";

Not to mention media queries, keyframe animations, or selectors, which would all be features that would be the next step after this.

My main line of thought is that either we're writing CSS in a string (which I'd say is preferable) or we inevitably have differences in the way we write CSS and CSS inside JS.

As a solution currently, you can implement a CSS tag and simply use

CSS `
    background-color: rgb(255 0 0);
    border-width: 0.5em;
`;

allowing you to parse the CSS however you'd like.

1 Like

I am not saying that whole CSS syntax should be in CSS but reasonable parts can be there.

And speaking about CSS grammar/syntax, as a person who implemented CSS and script parsers from scratch, here are some revelations if you wish:

CSS grammar has three list joiners (or groupings) (JS just one), all of them in order of precedence :

  • a / b - '/' is a pair joiner, in JS++ it will be tuple ["/": a, b]
  • a b c - ' ' is a "space list" joiner, in JS++ it will be tuple [" ": a, b, c]
  • a, b, c - ',' is a "comma list" joiner, in JS++ it will be tuple [",": a, b, c]

So this
border-top: 23px solid rgb(0 40 123 / 40%);
could be parsed to JS++ as:

[" ": 23px, "solid", ["rgb": 0,40, ["/": 123,0.4] ] ]

Right, but how does that improve the experience for me as a web developer? Why would I write

{
    border-top: [" ": 23px, "solid", ["rgb": 0,40, ["/": 123,0.4] ] ]
}

rather than

element.style.borderTop = '23px solid rgb(0 40 123 40%)';

I'm not conviced the new syntax improves the CSS-in-JS experience, let alone this syntax being useful enough to warrant a new JS syntax.

Let's look at a subset of your idea though; the dashes in property names. I've thought about that this would be nice, but this could be problematic when parsing JS. If we extend plain JS objects so that keys with dashes don't need dashes, then how would you read

const top = -23;
const css = {
    border: 0;
    border-top: 'solid';
};
console.log(css.border-top); // 'solid' or 23?

this would mean you can define keys with dashes without quotes, but not access them (and no destructure them). I'm not a fan of this inconsistency, and I'm not convinced it improves drastic enough upon the existing way (simply using quotes around a property name).

The semicolons also could be problematic - introducing them as separators (instead of commas) in normal objects could lead to confusion, not only for developers, but also JS parsers:

{
    foo: 'bar';
}

is already a valid statement - it's a block with one labeled statement, 'bar', but with your proposal it could also be interpreted as an object with one key-value pair foo: 'bar'.

All in all, I have the feeling this is a bit too broad of an idea and would lead to a lot of confusion for developers (it would for me, at least).

I am saying that this:

element.style.set { 
   border-top : 23px solid rgb(0 40 123 0.4);
}

could be a valid JS construct if following is met:

  1. to allow "white space joiners" at least in object literals;
  2. helper constants and functions like const rgb => (r,g,b) ["rgb": r,g,b] ;
  3. NNuuu support;

If you, as a web developer, think that embedded JSX is valuable then you may find embedded CSS valuable too. It is just that tuples + list joiners is the way to express as VNode (JSX) as CSSNode (CCSX) literals.

Perhaps I misunderstood then. Is the idea to invent a new language that allows this, or integrate it into Javascript? JSX is nice, sure, but it's a different language altogether, so that doesn't help your point too much.

Regardless of whether or not this is a new language, writing this

element.style.set { 
   border-top : 23px solid rgb(0 40 123 0.4);
}

would indeed be nice, but having to write

element.style.set { 
   border-top: [" ": 23px, "solid", ["rgb": 0,40, ["/": 123,0.4] ] ];
}

defeats the whole purpose of making CSS integrate into JS better. If this type of expression only happens under the hood, then I never have to know about it (just like I can use JSX without knowing what they are internally represented as).

I think overall it's best to either make this in a new language (so not make it native JS) or to use template literals for this purpose, like so

element.style.set `
   border-top : 23px solid rgb(0 40 123 0.4);
`

(and the fact that that's not being syntax highlighted is a different problem, but that has nothing to do with whether or not it's part of the language).

There does seem to be a lot of generic language changes required if we tried to make the CSS language valid syntax inside javascript. And a number of new global functions, like rgb(), and I would prefer not to pollute the global namespace with even more stuff.

Maybe, such specialized syntax is better suited as a sub-language to javascript. i.e. something like this:

element.style.set = css {
  border-top: 23px solid rgb(0 40 123 40%);
}

i.e. I'm tagging this object literal with "css" before it, to allow special CSS syntax inside of it.

...but, I would be perfectly happy if we just came out with a standard tagged template to parse CSS. If it was standardized, then editors could provide syntax support because they know what the "css" tag means, different css-in-js libraries could use it in different ways, and the language could provide some standard support for it too.

It would make it easier to inject variables into this syntax too. There's a lot of edge cases you have to think about with this new CSS Syntax idea, like variable interpolation, that's already solved with tagged template literals.

I almost wonder if JSX would have never existed if tagged template literals existed when they were designing React.

JSX is not a different language strictly speaking. It is just another format of literals that enables description of hierarchical structures disregarding of what it is - HTML, XML or whatever, so this:

let a  = <a href="foo">bar</a>;  

is just this:

let a  = JSX("a", {href:"foo"}, ["bar"]);  

where JSX is an arbitrary function that can be defined in user code.

In the same way JS object literals var a = {foo:1, bar:2} are actually translated (roughly) into calls like this

   var s = OBJ("foo",1,"bar",2);

We all use object literals in JS even they have distinct grammar from the rest of JS.

Example from my Sciter.JS where JSX is built into JS compiler natively. Here JSX function is defined for Mithril and here for PReact

Nothing in JS syntax and grammar prevents adding space as a list joiner operator at least in object and array literals so this:

var b = {
   foo: 12  rgb(0,0,0) solid
} 

to be compiled into:

var b = {
   foo: [12,  rgb(0,0,0), solid]
} 

Same thing with ; as separators in object/array literals - they can be used as , there so this can be valid object literal declaration:

var b = {
   foo: 12;
   bar: rgb(0,0,0);
} 

In some browsers there's already a CSS object attached to the window object. I don't actually recommend using it mainly because the browser support is really poor.
I'm not sure about it's capabilities. It's definitely part of CSS Houdini. I couldn't find much about it in the web. Might be worth checking out.
Houdini Draft
These discussions should be done there!

1 Like