Settings

Theme

CSS as a Query Language

evdc.me

81 points by evnc 25 days ago · 30 comments

Reader

k1m 25 days ago

I find CSS selectors a lot easier to write than XPath. I recently gave a talk on how PHP's new DOM API makes working with HTML and CSS selectors natively very easy (previously you had to convert CSS to XPath).[1]

It's a shame that because CSS is still primarily for browser use and styling, we don't get nice things like the ability to select based on text content like we can with XPath. My understanding is that this was proposed but didn't make it into the spec because it could lead to performance issues in a browser rendering context.

[1] https://speakerdeck.com/keyvan/parsing-html-with-php-8-dot-4...

  • lewisjoe 24 days ago

    What's more fun is: LLMs too are really good at CSS selectors. I've been building a document editing agent and I used this fact to present the document as HTML to LLM and make it query and pull pieces of documents into context by just specifying CSS selectors.

    Works like magic!

  • zerocrates 25 days ago

    Yeah, querySelector/querySelectorAll are totally widespread in client-side, it's nice to finally have them in PHP's newer DOM. Definitely what people are used to doing.

    • werdnapk 25 days ago

      document.evaluate is also widespread client-side.

      https://developer.mozilla.org/en-US/docs/Web/API/Document/ev...

      • zerocrates 24 days ago

        Sure, I was more speaking to usage... I'd have to imagine that querySelector* dwarfs evaluate in actual usage. Chrome's stats put evaluate at about 5% of loads; I'm not sure they have tracking for querySelector.

        Of course XPath has its place and is the better tool for some situations. On the PHP side XPath has been supported for a long time, while the querySelector stuff is quite new.

abathur 24 days ago

I feel like we need a name for css-the-syntax (and maybe -the-semantics) as separate from css-the-body-of-rules/functions/units/etc-defined-by-csswg.

There's juice in it, but it's hard to talk about and survey other uses without just searching GH for code using css parsers and just see what kind of shenanigans people are up to.

I've been playing around with a weird thing that's kinda like a template engine, but driven by a mix of a lightweight node-based markup language, css selectors for expressing what goes into the template, and a css-alike for controlling exactly how all of these parts come together.

  • moritzwarhier 24 days ago

    I think it's already pretty clearly separated in the standard:

    https://www.w3.org/TR/selectors-3/

    and this is what the DOM spec references, too (albeit at level 4):

    https://dom.spec.whatwg.org/#selectors

    So the common name "CSS selector" is already correct, or simply "selector"?

    "DOM selector" would be nicer maybe and not contain "CSS", but also I'm not even sure if it's better, because selectors in static CSS or selectors used with different DOM engine (XML parser, PHP DOM API, whatever) and static markup outside of JS engine means we're talking about a diferent DOM than the one exposed to JS.

    Also, some special selectors are directly tied to browser rendering and navigation, such as :hover or ::target-text.

    So yes, maybe a minimal subset of the query syntax could benefit from a name that's not tied to the browser and CSS.

    • Permik 21 days ago

      For the generic selector naming I'd suggest "cascade selector/selectors" as that gives a hint of the origins and describes the actual function of it pretty well.

      • moritzwarhier 21 days ago

        I see what you mean, but what would this do except add more churn?

        Words sometimes have misleading aspects, but I don't see any practical problem with the current usage of the word "selector" in web dev. The CSS part is often omitted when it's implicit.

        The spec separates selectors cleanly into its own module already, and there are already implementations that don't rely on HTML rendering.

        Any rename by commitee wouldn't stick anyway, and the origin of this selector spec is CSS, doesn't prevent other uses.

        When you bring in "cascading", you already go close to the CSS / rendering aspect, because that's the most common use case for cascading?

        Selectors don't cascade, rules do.

    • 9dev 24 days ago

      CSS containing Stylesheet couples it pretty tightly to styling things, though. What about CSL, as in Cascading Selector Language?

duncanfwalker 25 days ago

Reminds me of seeing this presented at a conference years ago https://github.com/braposo/graphql-css

It was a joke but I really like the way it pointed out how we copy and reapply patterns in different contexts and that might enable unexpected things.

  • evncOP 25 days ago

    oh this is fun

    > we copy and reapply patterns in different contexts and that might enable unexpected things

    yeah, that's exactly what I am trying to do here. Mostly it doesn't go anywhere, but it's interesting for the hacker spirit within me :)

    • nagaiaida 24 days ago

      yeah sometimes you end up making something very cool almost by accident. i tried to make shell pipelines properly concatenative like forth and friends and that had a lot of unexpected neat digressions in metaprogramming

spookylukey 25 days ago

The project pyastgrep https://pyastgrep.readthedocs.io/en/latest/ can use CSS selectors as a query language for Python syntax (default is XPath).

e.g.:

pyastgrep --css 'Call > func > Name#main'

  • evncOP 25 days ago

    oh this is neat! Feels like exactly the sort of thing I was gesturing towards. Thanks :)

shevy-java 25 days ago

Hmmm. I kind of like CSS but I hate the creep-up of complexity.

It's not that I don't understand the rationale - any programming language offers more power than a non-programming language. But I'd rather think here that something else could instead replace all of HTML, CSS and JavaScript, rather than constantly wanting to make everything more complex. I don't use most of the new elements in HTML5, largely because I don't see the point in using specialized tags for micro-describing a webpage. I succumbed to the "it is a div-HTML tag and it has a unique ID"; that's what I think mots of those containers actually really are. I even wanted to have aliases to such IDs, simply to use as navigational href intralink.

    [data-theme="dark"] [data-theme="light"] :focus {
      outline-color: black;
    }
And I also don't like this. It takes my brain too long to process all of that. It is no longer elegant and simple.

On the other hand:

   h2 {
     color: red;
   }
That is still simple.

    So ancestor(X, Y) :- parent(X, Y). means: “For all possible values of X and Y, X 
    is an ancestor of Y, if X is a parent of Y.”
See - I already don't want to have to think in such terms. What is the :- anyway, looks like a smiley.

    @container style(--theme: dark) {
      .card { background: royalblue; color: white; }
    }
I stopped there.

I think this is a problem with CSS - too many people are ruining it. It is strange to see how standards that used to work, are degraded over time.

  • evncOP 24 days ago

    yeah I mean, to be clear, I'm less proposing "What if we add even more syntax and semantics to CSS" and more "what if we steal ideas from CSS, notice their similarity to logic / relational query languages, and use them to build something new". I probably could have articulated some of this better.

    eg this example:

      [data-theme="dark"] [data-theme="light"] :focus {
          outline-color: black;
      }
    
    means, in English/pseudocode, roughly: "If you have an element X with attribute data-theme="dark", and X has a child Y with attribute data-theme="light", and Y is focused, then the outline-color of Y is black".

    so we could write this also as, e.g.:

      outline-color(Y, black) if 
        data-theme(X, "dark") and
        parent(X, Y) and
        data-theme(Y, "light") and
        focused(Y)
    
    that's Datalog, except I went ahead and replaced :- with "if" and "," with "and".

    if we want even more syntax sugar, we could do:

      Y.outline_color := black if
        X.data-theme == dark and
        Y.parent == X and
        Y.data-theme == dark and
        Y.focused
    
    imagine `X.attr == val` <==> `attr(X, val)` as a kind of UFCS for Datalog to make it palatable to Regular Programmers, right

    the declaration and scope of these variables is implicit here; if you want something even more ALGOL-family, we could write

      forall Y {
        Y.outline_color := black if 
           Y.data_theme == "dark" and
           Y.focused and
           Y.parent.data_theme == "light"
      }
    
    here we've explicitly introduced Y, and made one of our joins implicit, and it looks even more like Regular Programming now, except the Datalog engine (or equivalent) is kind of running all these loops for you, every time one of their dependencies changes, in an efficient way ...
    • evncOP 24 days ago

      one more, for completeness:

        SELECT 'black' AS outline_color
        FROM elements parent
        JOIN elements child ON parent.id = child.parent_id
        WHERE parent.data_theme = 'light'
          AND child.data_theme = 'dark'
          AND child.focused = true
      
      
      there's a lot of ways to express the same thing! it's interesting to notice the connections between them, I think, and their strengths and weaknesses, e.g. I probably wouldn't want to write my whole design system in SQL, but since it's relational queries over the elements structure and properties, you could.
efortis 25 days ago

Not sure I follow the scenario this would solve.

For instance, currently you can conditionally change a parent based on its children. For example, this `pre` could either have 16px or 0px of padding. Zero when its direct child is a `code` element.

  pre {
    padding: 16px;

    &:has(> code) {
      padding: 0;
    }
  }
  • evncOP 25 days ago

    tbh, this started as a connection of two disparate ideas ("hey, this thing looks like this other thing"), and then just kind of explores it in different directions.

    I think the conclusion (which I may not have made clear enough) is less like "These are limitations of modern CSS which ought to be fixed" and more "Maybe a CSS-like syntax could be added to a Datalog-like system and that would be helpful for making it more accessible to more engineers, navigating tree-shaped data, etc"

    thanks for the feedback, anyway!

  • tadfisher 25 days ago

    The article describes a syntax for modifying the underlying data (adding new child elements or attributes to the DOM) for matching selectors, not resolving style changes in a single pass like you've shown.

    • capitainenemo 25 days ago

      I suspect they are replying to this part of the article: "What you actually want to say is: “an element is effectively-dark if it has data-theme=”dark”, or if it has an effectively-dark ancestor with no effectively-light ancestor in between.” That’s a recursive relational definition. CSS cannot express it. CSSLog can:"

      The entire article doesn't seem to mention the existence of :has() which is rather surprising given how recently it was written. Not even in the footnotes.

      • tadfisher 24 days ago

        Yeah, that example is bad. The query doesn't require recursion, but they affirm it does by demonstrating a recursively-defined version of it. This is called "affirming the consequent"; "P -> Q" doesn't mean "Q -> P". Ironic, given the use of propositional logic throughout.

        • evncOP 24 days ago

          doh, good point. will fix this, I acknowledge I sort of handwaved the example. thanks for the correction!

aisalwaysa 24 days ago

LLMs suck at CSS now, very tempting to try this and see if its simpler for LLMs to reason about

logic-designer 24 days ago

i cant think of any real use for this -> but cool

lostinplace 24 days ago

Ummm... Isn't that just JQ?

securityTalent 25 days ago

Nice

zephyreon 24 days ago

This is neat, but no. /s

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection