A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

[Proposal] Expand CSS syntax so it handles DOM manipulation

bob
2020-05-25

Hey there,

By introducing the concept of a target selector into CSS syntax, and by adding some higher-level one-liner commands to handle common JavaScript functionality, full DOM manipulation for interactivity becomes possible with CSS alone.

This could potentially fill the need for a higher-level alternative and companion for JavaScript amongst developers, without changing the existing development structure of the internet.

This would be an addition branch to CSS, rather than changing any current styling-oriented direction of CSS. A branch for the syntax itself.

I humbly present a working prototype that I’ve spent the last couple of years on:

https://activecss.org

I would love to know what you guys think. Personally I love it - for me it’s a very intuitive way to code. I hope that it might spark some ideas at least.

Rob Galbraith, Software designer, River Zend Dynamics Ltd.

trusktr
2020-05-26

This is interesting. Can you describe how it is better than custom elements in JS (f.e. with respect to the tic-tac-toe example)? It’d be nice to see a comparison of the two approaches and how using activeCSS improves the experence.

bob
2020-05-26

First, thanks for checking it out! It is a different way of coding for sure, so I think a walkthrough comparison would be a good thing, especially for web components, as you suggest. What I’ll do is set up a page on the website that compares the two approaches step by step, something like that.

I will say here though, the main DX improvement though is the intuitivity of coding. I’ll do a comparison with CSS.

With CSS, you don’t have to think particularly to assign a style - you just write it, you know? CSS is very quick and intuitive once you know your selectors, commands and the hierachical rules. CSS is generally pleasant to code in. Fun even!

An analogy, would be to imagine a world in which if CSS didn’t exist and you had to write all your styling and cascading in JavaScript. It would take ages to write, would get messy in the scripts pretty quickly, and this ultimately would put people off from native styling. Imagine tweaking that code - nightmare! So you’d do the minimum or use a CSS framework that someone else had done and leave it at that. You wouldn’t bother with tweaking CSS so much, as it wouldn’t be productive and not best practice to “reinvent the wheel” on styles, etc. etc. Everyone would use something like bootstrap, and CSS styling would be left to the CSS framework developers, “who knew what they were doing”. Or a lot of websites would look like old windows programs, with the default grey styles assigned to objects.

CSS solves the styling complexity by providing a direct way to assign a property to an element, and provides sufficient styling properties to handle all the common requirements necessary to style elements and make things look nice. CSS syntax looks a bit weird - it doesn’t look like regular code - but it surprisingly is very good at what it does. It abstracts out the JavaScript styling API, so you can style in a very quick and direct way. There is no more direct way to style an element - it gets the job done very quickly.

CSS makes things a lot quicker. CSS wins every time - no argument. No one is out there trying to reinvent CSS.

Currently, we have a similar situation with DOM manipulation. Anything too complex is left to the plugin or framework developers, and best practices become “use a framework” and “don’t reinvent the wheel”. Right now, you’d need some sort of recent CS degree to keep up with progress in UI frameworks. We have the same symptoms in the community that would exist for styling if CSS did not exist.

For what? Adding and removing elements? Changing styles and properties? Updating content in an element? It shouldn’t be rocket-science.

The thing is, these current over-engineering symptoms only really exist because doing complex interactivity yourself on a webpage is currently a lengthy process, and practically it’s not worth doing it without some plugin or framework to help out. Even if the reality is that native JavaScript is often all you need. It is still too lengthy a process for many developers to bother with. So we end-up with clunky one-page websites.

But all that is needed to solve it is some higher-level abstraction.

So Active CSS solves the same problem that CSS did, in exactly the same way that CSS did. It really isn’t anything new - it is just an extension of CSS.

With Active CSS, you feel you can tweak interactivity, add a bit more interactivity, like you can add more CSS, or tweak CSS. It feels exactly the same as CSS when you code with it, as it is quick and so direct. There is no waffling about with the JavaScript API.

So in practice, that is probably the most notable DX improvement.

But it only handles what it handles. Active CSS has its limitations in the same way CSS has its limitations. It only addresses interactivity - not general programming. You still need JavaScript.

trusktr
2020-06-10

that would be sweet! It’ll really help the average web developer understand how the paradigm compares to the vast majority of framework out there (f.e. we have custom elements, but those are similar to all the other frameworks like React, Angular, Vue, Svelte, etc, that all give you a way of encapsulating pieces of UI in classes or functions so to compose them together). So web devs familiar with this all-to-familiar pattern across many frameworks and libs would benefit from having a clear understanding of how this new paradigm compares to the component class/function paradigm (where CSS is encapsulated withing the class/function comonents).

ActiveCSS looks neat, but the DX improvement may not be immediately obvious if newcomers that step into it aren’t already thinking in another direction than they do with all the existing libs/frameworks.

bob
2020-06-10

Yes, I agree. It’s a major paradigm shift. So from your last comment I started writing a full tutorial on an enhanced version of the React tutorial, so Tic Tac Toe with time travel, plus some other cool things. It’s taking a bit longer that I thought it would - there’s quite a lot to it, as you can imagine. It’s on its way… I’ve had to digress onto fixing a couple of bugs and setting up inline “styling” functionality, but hopefully that will be all wrapped up soon. I’ll aim for next week to get the tutorial and paradigm comparison up, or thereabouts. Having said that, there’s a couple of syntax changes I want to make, like having a @command function for creating a command, as it looks strange having full JS embedded within a CSS command, and the same @ function treatment is needed for creating a conditional and running a JS function. So I may knock these out before publishing the tutorial. Anyway, thanks for the feedback so far - it all helps to keep things moving. :slight_smile: Really appreciated.

matthewp
2020-06-17

This looks really interesting. Some prior art here is the Decorators proposal as part of the original web component specs. It allowed some DOM manipulation via CSS but this seems to go quite a bit further. Reference: https://www.w3.org/TR/2013/WD-components-intro-20130606/#decorator-section

trusktr
2020-07-02

Interesting, so <decorator> was sort of to HTML as what <defs> is to SVG.

trusktr
2020-07-02

Cool, looking forward to that demo!

bob
2020-07-04

Cool :slight_smile: Sorry for the delay. Got half-way through the demo - modelling it on the React one, then found a major bug with deep-referencing reactive Active CSS variables, fixed that, then realised components needed to be codable without a shadow DOM as I suddenly had a relatively non-techy client at work on one of my CMS’es that used Active CSS complain that everything (basically the Active CSS core) was broken on the old Edge which was his main browser (obviously - the old Edge doesn’t support shadow DOM), and as that version of Edge doesn’t update itself I had to quickly make a version of the core that worked on non-shadow DOM browsers, and then realised this whole thing may put people off using shadow DOM for a couple of years and not want to build components at all. So it really depends on the user base and the type of website whether the shadow DOM solution for components can be used or not.

I fixed basic old Edge support in the core but I’m aiming to put a release live at the end of Sunday that allows the same components without a shadow DOM and will work on the old Edge. Obviously all CSS is global - can’t do anything about that, and that’s not particularly a bad thing anyway. But there are now private components with isolated variables and also generally scoped components that can be hosted inside private components or whatever. Any events assigned to any component are always isolated. These components are then able to be hosted to custom elements or regular divs or whatever.

For simplicity’s sake, the only difference in the implementation of the components is that private components have a “private” parameter, shadow DOM components have a “shadow” parameter, and “global” components have no parameter at all. Otherwise everything is coded pretty much exactly as it is at the moment on the current release, with the @component {} wrapping syntax. I’m just wrapping up a “button counter” example page where all the different types of component combinations can be compared, so it should become clear what the difference is.

It all works offline except that the global components don’t allow isolated events yet. Hopefully sorting that out today - the docs are mostly updated, then will be back onto the bloomin’ Tic Tac Toe tutorial.

Will post back on here when there’s something to look at - as I say, hopefully by the end of Sunday. I didn’t want to do a full tutorial on components without having this functionality stable, hence the delay.

Thanks for the interest trusktr and matthewp!

trusktr
2020-08-21

Cool. Looking forward to seeing it still. :slight_smile: No rush though; I never expect deadlines for one-person free-time open-source projects. It’ll be ready when it is. :slight_smile:

bob
2020-08-22

Thanks :slight_smile:. Sorry for the delay. I’ve had to do a bunch of upgrades on the road to the tutorial. It’s looking good but a couple more things do need to get implemented before a definitive tutorial can get done… There’s nothing worse than doing a tutorial and going “But why? That’s harder than doing it natively.” So I’m trying to mitigate that response…

excitedbox
2021-03-09

This is a great example and proof of concept. I think some of the later examples get a little harder to read but overall an amazing project.

I actually posted a proposal about this same thing on the UIevent spec git a few days ago asking for an Actions spec in addition to the Events spec.

I totally agree that we need to have some way of updating the DOM without using full JS.

vrugtehagel
2021-03-16

First of all, let me start off by praising you for this amazing project. It looks very extensive and well thought-out. Hats off to you!

Now, allow me to give you some constructive feedback on the project, hopefully you can use this to improve activeCSS even further.

When I first saw activeCSS, I was presented with CSS’s :hover and how that, in activeCSS, becomes :mouseover. This feels very unnatural to me, because in CSS, pseudo-classes are used to denote a state of an element, not an event. This line might be blurry for :hover, because the :hover state is, in a way, a result of a :mouseover event, but it becomes abundantly clear when looking at some other pseudo-classes (for example, :empty, :fullscreen, or :invalid).

I also was under the impression that it would be an addition to CSS, not a separate language (it is called activeCSS, after all). However, I quickly realized this is not the case. This makes me a little sad - this would mean that if I wanted to use activeCSS, I probably still need CSS, and good chances are, I still need JS (though I admit I have not tried to make something complex with activeCSS) so this adds another layer of logic on top of (or, well, in between of) the layers projects already have.

As a small interlude (I wanted to get this out there) I especially love the :clickoutside event - great addition.

Also, I admire that you’ve taken the time to integrate support for web components. However, I’m not convinced from the examples you provided that they are any easier to write than regular web components without activeCSS.

From what it looks like, you’ve drawn some inspiration from SCSS in some places. This is great - SCSS offers some features that translate quite well into activeCSS. On the other hand, be careful to not look like SCSS too much - people (myself, at least) might just get confused between the two and get frustrated when a feature from SCSS doesn’t work in activeCSS (mixins, for example, or the way variables are declared).

I also took some time to read through some of the examples on the site. I have to say - they took me quite some time to wrap my head around. You mentioned it was very intuitive to write for you, but often it’s more important for a language to be easy to read rather than easy to write (I know, I’m new to activeCSS, so this is probably a contributing factor to me having some trouble, but still I think this is a valid point). I also noticed that it looks like it’d be quite hard to get things perfect - to elaborate on what I mean by that, let’s look at the “Clock with variable binding” example on the site. When the clock is running, I can mouse-down outside the clock, move my mouse over the clock, and mouse-up. This results in the clock being greyed out, but still running. We can do similar things to make the clock green-while-not-running. This is exactly what activeCSS told it to do, so in that sense it’s good, but to get that right requires quite a bit of work (not necessarily much more extra code, but more thinking and more code pollution) because the styles are so heavily tied to classes.

Those were my initial impressions. While activeCSS’s syntax seems a bit complex at times, I do love the idea of doing simple DOM manipulation such as toggling a class or setting an attribute through a CSS-like language, and I applaud you for taking a shot at it (and getting very far at that).

bob
2021-03-16

Thanks! Yes, I agree, the examples are a bit hard. I’ve tended to write examples while developing the functionality itself and as a result they aren’t always easy to follow and can get confusing to read. Point taken and thanks for the feedback!

bob
2021-03-16

Thank you! Yes, I’ll will take up these points - they are all good.

  1. The blurriness between pseudo-selectors and events. I’d not even considered that to be an area of confusion, so that’s something I need to think about and make clearer so that the change of viewpoint can be a bit easier. I guess it’s too jarring a whammy to lay on someone right away…

  2. Yes, it is a separate language. With the view to be one day in the browser as part of CSS. I needed to make that clear, otherwise it could be looked on as just another framework. It isn’t a JavaScript framework - it’s supposed to be a prototype for ideas to end up being an extension of CSS so we don’t need to use a framework - hence the leaning to the term CSS. Regarding everything in one file, I could extrapolate the active css from regular css files - it did cross my mind - but that would cause immediate styling errors in the browser when first loaded. If integrated into the browser, obviously that wouldn’t be case. It has to be in separate files to avoid these errors. I actually think it’s nice to separate concerns though, anyway, like have events with dynamic styles in one file, static styling in another. Regardless, the main reason I haven’t done it is because it causes styling errors…

Actually, I could allow static CSS into the Active CSS and place them into stylesheets on-the-fly… Hmmm… You’ve got me thinking there. Ah - no it’s not going to work, I remember now - there will be a delay, even if slight, in rendering that CSS after the page has loaded once Active CSS has fired up. Yeah - that’s not going to be practical. The ultimate solution for everything to be in one file is for it to be implemented into the browser - that is the solution to the problem.

  1. Yes, the clickoutside event can be done! I’m just about to do a release which allows it to work in shadow DOMs, as that’s busted at the moment. I’ve also implemented the “<” (closest parent) CSS operator too! I figured if it’s needed in JavaScript I might as well implement it. I get that it’s not performant for the rendering engine, but as a dynamic tool it makes total sense. (I use that < operator in the todo example which I’m planning on releasing with 2.5.0 at some point tomorrow).

  2. The web component examples are too complicated - that’s main trouble. As I’ve been developing functionality, I’ve written the examples to have everything and the kitchen sink in there in order to get everything to work. So the examples haven’t been straightforward.

When I read CSS written by some of the CSS wizards - you know - the guys who can draw the Eiffel Tower in one span tag and 3 SCSS commands, I get completely lost too. Like totally lost. My maths and geometry skills are completely terrible. I look at any of their examples and think I’m completely hopeless as a person. But the underlying basics are simple, and if each one is explained then everything becomes understandable. The basic CSS syntax is inherently uncomplicated and so is the web component syntax, so I think that it’s just the examples to do with web components that are complicated rather than the underlying syntax. There’s good and bad ways to organise things, and I probably didn’t do the best job there in working out the web component examples. But I think the fundamentals are there. I’ll try and write some clearer examples.

The ultimately good thing is, is that this is just a protoype for something to be implemented into the browser - it isn’t set in stone - god no! I’ve had little feedback from the community on specific points, so I’ve just ploughed ahead using my noggin skills and that’s about it. The syntax can be changed for sure, and it would be a miracle if everything was right about it first time and just got transplanted into the browser as it is. I would hate for it not to be considered just because there is too much there or because I got a few aspects wrong - that would be nuts.

  1. Not sure what to do about the SCSS influence - there was a little influence, but not a lot. I’ve not used SCSS that much TBH, beyond variable use and nested structures. That’s about all I’ve needed to use it for. I tend to keep my CSS pretty basic as I find it easier to maintain. So not sure what to do about that. I guess I could document the differences, but I’d have to look it up :slight_smile:

  2. Clock with variable binding bug. Congratulations! You are the first person to have found a reported an bug to me in 14 months! All that’s needed is a class on the event selector to stop the event from happening - I’ll put that in.

I could re-write it using state instead. But I wasn’t sure if that was over-complicating things. I dunno - do people prefer state manipulation to direct class manipulation? One’s old school, and the other is new school, and both have their logicities about them. Maybe I could do an example using state instead, like right underneath it. I’ll take a look at it.

[edit] I’ve just released 2.5.0 with a new clock example that uses click instead of mousedown and mouseup - it gets rid of the problem. The problem wasn’t related to Active CSS - more to do with just issues using those events which would have happened anyway. So here is the clock with variable binding example - it should be easier to read now:

https://activecss.org/manual/clock-with-binding.html

div:draw {
	~clock { trigger: Tick; }
	render: "Time: {{clockTime}}";
}

~clock:Tick {
	var: clockTime new Date().toLocaleTimeString() every 1s label clockTick;
}

div:not(.clockOff):click {
	cancel-timer: clockTick;
	add-class: .clockOff;
}

div.clockOff:click {
	remove-class: .clockOff;
	~clock { trigger: Tick; }
}

div:click {
	add-class: .clockPress;
	remove-class: .clockPress after 100ms;
}
  1. Yes, even I think some of the examples are complex. TBH I could do with some help with creating the documentation. It’s a real skill to do it well. I think I need to revisit everything and only address one thing at a time in all the examples.

I mean, technically, all the active CSS commands are one-liners. It’s not fundamentally hard in concept. I think it’s just how the docs are at the moment. Like JavaScript and CSS, and even HTML sometimes can be hard to read, so I think that’s the area I need to work on.

  1. Version 1 had only the simple DOM manipulation and was great! You can do a lot with just that. The docs website was built on basic version 1 syntax (which hasn’t changed since). But when I released version 1 a year ago I realised that the whole project wouldn’t be taken seriously unless there was a solution to web components. Unless components are addressed in a simpler way, why would anyone switch to native solutions? People, rightly or stupidly, want components. Hence the major expansion that followed to try and get a CSS equivalent. But honestly I’d be happy with only a few basic commands from active CSS implemented. There’s about the equivalent of 30 years of CSS evolution crammed into 2 years of work, so it can definitely appear overwhelming at first glance - and even appear nothing like CSS. But just one or two of the simple commands announced, like you say, would be a big help to developers all by themselves.

Thank you for your comprehensive review. You shall be placed on the credits page under the Positive Comments section along with Håkon :slight_smile: I’ll post here again when I’ve sorted out the docs.

bob
2021-03-17

One other thing on this in case it isn’t clear, regular CSS can be used everywhere in Active CSS, so it definitely isn’t a completely separate language - you just can’t put static (ie. non-event triggered CSS) CSS in the same file because of browser errors during page load.

So you can do this:

button:mouseover {
    background-color: green;
}

You can put delays on regular CSS commands like all the other commands in Active CSS:

button:mouseover {
    background-color: green after 2s;
}

You can also put timeout intervals on all commands too. So in version 2.5.0 coming tomorrow you could do this and get random colors every 2 seconds:

button:mouseover {
    background-color: #{$RANDHEX6} every 2s;
}

Note: {$RANDHEX6} is a new flexible variable constant in Active CSS. You can also do this {$RANDSTR0-30} to get a random character string from 0 to 30 chars, {$RAND0-255) to get a random number from 0 to 255, {$RANDSTR10} to get a random 10 character string, or combinations like that. I implemented it because I kept having to write random number generators, and I wondered how many other developers have had to do the same thing over the years. Older GUI languages like Visual Foxpro implemented things like this into their language and they can be incredibly useful. Hence the new variable type. You could have a similar shorthand function implemented into native JavaScript which would be good too.

Visual Foxpro was actually something that influenced me quite a lot at the start of the project - the ability to easily add methods to drawn objects (like fields, dropdowns, buttons, etc.) on the screen. CSS is definitely, unquestionably, without any possible argument, the easiest way to add properties to the HTML objects, so I literally just wondered what the syntax would look like if methods could be added too following a similar syntax, and just went from there. On a webpage all the main action is actually happening on the webpage, all the objects are already on the page. And the browser handles it’s own objects in a really good way. The browser is just another GUI environment. So I think it’s worth checking out approaches taken in the old GUI OO languages to see how things can be done with those objects - like I’ve just taken the approach of attaching methods to the HTML objects as the main focus of GUI development on the webpage, like I did in the old days (I dunno if Windows GUI developers still do that or not - they probably do).