Hacker News new | past | comments | ask | show | jobs | submit login
Tree views in CSS (iamkate.com)
404 points by divbzero on Nov 19, 2022 | hide | past | favorite | 69 comments



This is very clever, and it’s nice that it’s fully accessible and requires no JS.

However, looking at that final CSS just makes me wish that we added new interface elements to HTML far more often. Folks run rings around themselves to implement things that have been in any desktop UI toolkit worth its salt since the 90s.


On this case, the functionality is there right on the HTML, and the CSS is just customizing the appearance to make it look like a desktop widget.

This is not an issue of lack of HTML support. In fact, this is exactly what CSS is for.


There are so many ARIA guidelines on implementing alerts, modals, comboboxes and anything in between. Yet most don't have any equivalent presentational or functional counterparts in the existing browsers. Writing this comment, just as I'm writing an accessible combobox in a project where I'm not using any frontend framework and minimal JavaScript.


Let’s extend this thought a bit deeper. Why are we consuming different brands of fixed-form content through bespoke interfaces? Why is there no standard for navigation of any type of content?

I really hope RSS is coming back in force.


Perhaps the the Open UI unitiative is a good place to create a proposal.

https://open-ui.org


They are really bad at first impressions. Do they not think about their own code? Literally the first thing I see when I visit the page is a misused <blockquote>

> The blockquote element represents a section that is quoted from another source.

— the W3 blocquote specification

Their copy isn’t quoted from another source, it’s a janky admonition writers of Markdown use when their tools, Markdown, don’t support the semantics they mean. <blockquote> does not mean “put this in a box”.

Click the navigation and the first things you see are links to GitHub and Discord, two very not open platforms—which should go against the philosophy of the project if they want open standards.


> A tree view (collapsible list) can be created using only HTML and CSS, without the need for JavaScript

A bit tangential, but so much more should be done in CSS rather than resorting to JavaScript. One gripe I've always had are hamburger menus that fail to work with JS disabled. Most of my browsing is done with Safari on iOS and I've disabled JS on that browser since 1) I enjoy the privacy benefits 2) popup modals and other distractions are non-existent.

You can code a hamburger menu that opens when you click on it, without JS. Yes, CSS is not just for styling it can also be used for functionality.


It’s worth noting here that it’s HTML that’s doing the actual heavy lifting here, rather than CSS—the CSS is just making it look prettier.

Something like the venerable checkbox hack is a case of CSS providing the functionality. That’s imperfect.

<details> is HTML providing the functionality. That’s the ideal.


99% of users don’t make a fuss about javascript and the developers know. It would probably be wrong to get so distracted by satisfying a tiny sliver of user base (the pickiest sliver?) with a disproportionate share of work.

Which is a shame because there’s something very elegant about using CSS. It’s usually much more performant.


The majority of those CSS "hacks" are not at all accessible, however. You'd get to browse the web without JavaScript, and millions of others would not be able to browse the web at all.


This is what’s often (OK, almost always) missed with such JavaScriptless components: progressive enhancement with JavaScript, so that it works without JavaScript, and works better with just a little JavaScript. Most of the traditional CSS hacks can be made just as good as the JavaScript version designed with accessibility in mind without too much effort. Most of the time it’s just managing some ARIA attributes and no more. Occasionally you might add an extra supporting element or slightly restructure an existing one.


Is that a problem with the browsers not providing functionality that should be standard or is it a problem of custom components that need the JS for the dev to specify how they work?


Not entirely sure if I fully understand your question.

To clarify, these CSS hacks often use checkbox inputs for isomorphic state management and that is simply incompatible with the semantics those elements are designed and expected to represent.

ECMAScript is a browser standard and was specifically invented for adding interactivity like toggling a hamburger menu. Yes it's also used for less savoury business but that's not the end-user's fault and shouldn't be made into their problem.


> To clarify, these CSS hacks often use checkbox inputs for isomorphic state management and that is simply incompatible with the semantics those elements are designed and expected to represent.

The one mentioned in the article, does not use checkboxes.

It uses functionality built into HTML itself


browsers do provide that functionality, just with JS.

CSS is not designed, to be something you're supposed to write Doom in even though you can. It hasn't been really updated with the purpose of 'obviate javascript' because the browsers don't really want to do that.


I made another totally different kind of "tree view" in CSS a while back, in this case to see some kind of JSON-html tree into a tree:

https://francisco.io/demo/tree/

It was a fork of a much earlier project but I improved it a lot IIRC; unfortunately I lost the original source and now chances of finding it are very small (unless someone here knows it!)


very cool. Is it possible to collapse “sidebar” to “main”?


This summer I made a website to show the closest common ancestors of arbitrary species. The hardest part was to get the tree to look nice. It's still not perfect. A naive approach gives a very wide tree.

https://sol.vandenoever.info/


I worked on a big project showing arbitrary family trees. Collapsing what are actually graphs into visually compact trees is very hard! Nice job


Wow, this is a really nice little tool! I usually end up manually traversing Wiki entries to get to this info, so it’s a great shortcut!


My app has some huge tree (CRUD-able). Say 20k-30k nodes (sweet spots for being usable without virtualizaion). And Chrome perf sucks real bad for "Recalculate Style" phrase" the `.tree ul li::before{content:''}` is going to be ruining perf. Meanwhile firefox is wicked fast for overall perf for lots of dom nodes.


Would you consider using d3.js? I’ve been able to get great performance for large deeply nested trees using d3. It depends on your use case of course.


It's 278.58kB(.min), I'm not sure how good its tree shaking is. I pick every js dependency seriously. Generally libs < 7kB gzip for each.


I did something similar a while back in Github Issues markdown to show stats for files in the codebase that https://github.com/grafana/grafana/issues/46451#issuecomment...

Used nested details/summary, but not ul/li - It can be quite tricky to use the right level of whitespace to get GFM to render what you intended.


Very neat! Any ideas how to add correct vertical line heights with multi-line / variable-height items? e.g. the vertical line next to "Jupiter" here is too short https://gist.github.com/refset/c6f3ac05cacc12d547b4bc96bbeac...


This one works with variable height, you can pull some css from it https://codepen.io/khoama/pen/AoPeMM


Thank you! I guess the trick with this suggestion is to move the vertical line to directly beneath the text such that it doesn't need to be taller...which is probably a good enough workaround, even if not exactly what I was hoping for :)


Cool Kate and thank you for this smart code. You could have a look to your code rewritten in my small lambdaway project : http://lambdaway.free.fr/lambdawalks/?view=viewing_trees


    {{tree} 
     {li
      {details open
       {summary Giant planets}
       {ul
        {li
         {details close
          {summary Gas giants}
          {ul {li Jupiter}
              {li Saturn}
        }}}
        {li
         {details close
          {summary Ice giants}
          {ul {li Uranus}
              {li Neptune}
        }}}
    }}}}


For what it's worth...

In Sciter you can simply define tree view as <select type="tree">:

   <select type="tree" treelines>
      <option>
           <caption>Giant planets</caption>
           <option>
              <caption>Gas giants</caption>
              <option>Jupiter</option>
              <option>Saturn</option>
            </option>
            <option>
              <caption>Ice giants</caption>
              <option>Uranus</option>
              <option>Neptune</option>
            </option>
        </option>
    </select>
And you will get: https://sciter.com/wp-content/uploads/2022/11/select-tree.pn...

Tree view is quite popular in desktop UI, so I've decided to add it as built-in.

And needless to say, that rendering of a tree is just one part - it should also be keyboard navigation and other a18y things.

As of tree lines themselves:

  :scope[treelines] option > option 
  {
    display: list-item;
    list-style-type: tree-line; // <<<< Sciter's CSS++ 
    list-marker-size:1px;
    list-marker-color:#ccc;
    list-marker-style:solid;      
  }


I recently used details and summary for a tree view in react and ran into this bug

https://stackoverflow.com/questions/74313572/what-does-this-...

Just passing on info if you decide to use details in react


You’re complicating the two elements; They are interactive without javascript. Your answer is listening to native “open” change event, then reading this value and setting it back to the attribute via JSX. If the event happens at the wrong time, you’re possibly reading the previous value (closed) and setting it back to the details element.

I’d either avoid details in your case or see how others are using it.


I am not, as I need to state of the details to be settable. That's the whole mo of react, that all UI state is under your control and has a single source of truth. For example you want a tree of files, give the user a search function, and want to expand/collapse any subtreee that contains/does not contain a result.

Details is a collapsable building block. that's it.


This is not what is typically called a tree view: this is collapsible nested lists. The differences are important, and very fiddly to reconcile.

The key difference is that in a tree view, every node is focusable and has an action associated with activating it (which for parent nodes might be expanding/collapsing, but need not be), whereas with collapsible nested lists, only parent nodes are focusable, and their action is reserved and limited to expanding and collapsing. (You could make leaf nodes focusable and give them actions—e.g. make each one a link—but you can’t give parent nodes any other action, which is a fundamental limitation.)

Here’s what tree views are: https://w3c.github.io/aria-practices/#TreeView. This describes the functionality and interaction modes (including all the expected keyboard behaviours), with examples.

One important secondary difference: the article said that “the standard keyboard interaction is supported automatically”, and this is true for collapsible nested lists, but false for a tree view. In the former, each parent node is separately focusable, and you expect to use Tab/Shift+Tab to navigate between parent nodes; but tree views are composite widgets that manage focus, only appearing once in the tab order and using other keys (like the arrow keys) to manipulate focus within.

Is it possible to implement a true tree view with <details>? Mostly, yes, but there are limitations and difficulties.

If you’re OK with parent nodes’ only action being to toggle expansion, it’s not particularly hard; do it largely as shown in this article, making each leaf node focusable, and add some (nicely optional) JavaScript to get the keyboard interactions right and give the right ARIA attributes. The ARIA Authoring Practices document already linked will guide you through approximately all of the considerations. (If you haven’t worked with this document before, please do read the “Read Me First” section first.) In fact, I strongly recommend this general approach: make something that functions with only the HTML, then enhance it with JavaScript.

If you want parent nodes to have actions, parent nodes’ actions will need to be moved out of the <summary>. You’ll end up with something like this:

  <ul class="tree">
    <li>
      <a href>Giant planets</a>
      <details open>
        <summary>Expand</summary>
        <ul>
          <li>
            <a href>Gas giants</a>
            <details>
              <summary>Expand</summary>
              <ul>
                <li><a href>Jupiter</a></li>
                <li><a href>Saturn</a></li>
              </ul>
            </details>
          </li>
          <li>
            <a href>Ice giants</a>
            <details>
              <summary>Expand</summary>
              <ul>
                <li><a href>Uranus</a></li>
                <li><a href>Neptune</a></li>
              </ul>
            </details>
          </li>
        </ul>
      </details>
    </li>
  </ul>
Focus and accessibility management will become a little more complicated, but it’s still workable. But styling will be at least somewhat painful because of the DOM structures and the order of things. You’ll probably want to use details::before for the marker. Grid and `details { display: contents }` might help with layout, though absolute positioning is probably the pragmatic approach (and test cross-browser before doing anything like `details { display: contents }`, the two features can have surprising interactions). Also in the absence of JavaScript, it’s not going to be possible to get the expected interaction order: you would want the focus order to be expand/collapse button, then the node itself, then children, but those first two will necessarily be transposed. Still, it’ll be better than it not working at all.


I love the hacker thought process of "Is there an HTML element that can hide and show content right out of the box (without js), and now can we borrow it to implement a completely different data structure that wasn't obviously part of its design?"


In old days (around the time when Yahoo switched from table design to css layout) people were hacking this with :hover. But mouse had to stay in the 'opened' content.


I love that :hover trick, a well placed tag in back and the mouse does not leave the "opened" (active) content that easily. There is also :focus.


Except that native menus don’t really work like that. They have a nontrivial interplay between hovering and mousedown/mouseup that you can’t replicate with just CSS. Not to speak of the missing keyboard functionality.


Native menus? When I need native menus, I use a GUI toolkit, you might have put too much of your concerns into my words.

When I do "native" Menus in HTML I use lists und you can skip to navigation on top if you like but content comes first. And yes, the site works natively in a textmode browser.


Versus the native GUIs approach of here is a control for doing what you want, and here is a clean architecture to design you own controls.


The native GUI approach seems to struggle with extending the behaviour of stock controls, Win32 most of all. Granted, this is a very fuzzy problem that’s much more difficult than it seems (how do you shouls a CJK-style IME interact with a formatted-alphanumerics input?), and HTML+CSS doesn’t really do better, but it’s also a problem people have, and I don’t think native toolkits have a convincing approach. So I’m not ready to declare UI toolkits solved.


Is there a good JS part, a tree manager that comes close to a standard component? I`ve been looking for one the last few days. The closest i've found: https://www.naiveui.com/en-US/os-theme/components/tree

Probably with html5 draggable and the amount of variation that it is hard to make a standard component.


I made one specifically for viewing & editing json. Has draggable reordering with rough rules, auto-editable-values, copes with live updates whilst user is using it and other bits and bobs. Maybe along the lines of what youre looking for? https://github.com/NewChromantics/WebComponent_TreeView


That's so cool! Where can I find more similar topics on JS-free implementation of "tree", "fixed table header", etc? Thanks!



Are summary-details ok to use yet? Last time I've checked, ammittedly some time ago, browsers had bugs and quirks around them.


• IE and EdgeHTML never supported <details>, so until comparatively recently you had to polyfill it. Now, you need not worry about this, because you should actively not support IE or old Edge.

• WebKit still doesn’t use ::marker on summary. If you’re hiding the disclosure marker, you can’t just use `summary { display: block }` or `summary::marker { display: none; }`, and must remember to `::-webkit-details-marker { display: none }` as well. And if you want to change it, you can’t just change its list-style-type or manipulate ::marker, but must clobber the whole thing and use ::before or similar.

(Aside: if you don’t use a Mac, Epiphany is a good WebKit-based alternative to Safari on other platforms that will exhibit most of the same behaviours and quirks as Safari.)

Other than that, it’s all pretty solid now.


May I also recommend Falkon, as an Epiphany alternative! Really hoping the best for both projects!


Falkon uses QtWebEngine, which uses Chromium. That’s no use as a Safari testing alternative.


You're right. Since it's a KDE project I assumed they would be more aligned to WebKit's roots, sad.


Thank you!


any issues should be easily enough to polyfill


Css rendering issues weren't


I like this a lot. I wish sidebery (firefox extension for vertical tabs) had a style like this. It's all I've ever wanted from vertical tabs but no one seems to get this right. If I could do it myself...

Here's to hoping I can figure out how to make a react component of it :)

(Thank you for showing us this!)


The addon Tree Style Tabs has the option of adding your own CSS to style the tree view. I don't know the the HTML that renders the tabs is built up, but I'm confident that it's possible to replicate this style with some CSS tweaks.


Also seems useful for comment trees.


IIRC there was a Reddit alternative frontend that tried this to avoid using any JS and counterintuitively performance was awful. I think because this is somewhat obscure it's less optimized by browser vendors than DOM manipulation via JS.


> margin-left : calc(var(--radius) - var(--spacing));

Can you use calc in modern browsers these days without transpilers?


Yes, all major browsers support calc() and have for a while now

https://caniuse.com/calc

https://developer.mozilla.org/en-US/docs/Web/CSS/calc


Best post this week on HN. Love it.


It can actually be simplified further by skipping the <ul> and <li> elements and only using <details> elements:

https://twitter.com/marekgibney/status/1593950777739218947


doesn't that make the desired styling impractical unless you replace them with divs? I dont think that would simplify it unless i'm mistaken


Do you mean the lines and the round buttons in the article?


yes, that styling was what the article was mostly about after all.

The pure html was <10% of the article, the remaining 90+% were about the styling


The styling is done via borders and ::before and ::after content applied to the nodes. Why would you not be able to do that with <details> elements?

Here is a fiddle with the first step (borders on the left) applied:

https://jsfiddle.net/qvb3udj6/


I think probably the more concerning thing here is that you're removing the semantic value of HTML, in general I think you want to use the element that best suits your content. Lists make it easier for crawlers and screen readers to understand your website.


I would be interested to try that out.

I just went through the whole first page of Google results for "screen reader simulator" and none worked without installing extra software.

Is there a website, where you can test how a given piece of html is interpreted by screen readers?


Use Narrator, Window's built in screen reader, or, even better, install NVDA. It's free and open source. It's also the majority screen reader on Windows. (If you're on a Mac, you've got VoiceOver built in.)


Here is a good summary: https://accessibility.princeton.edu/how/content/lists

WAVE is a great browser extension to have for frontend developers: http://wave.webaim.org/extension/

It helps you identify aspects of your design that might not be accessible.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: