Tag: Smarter

Smarter Ways to Generate a Deep Nested HTML Structure

Let’s say we want to have the following HTML structure:

<div class='boo'>   <div class='boo'>     <div class='boo'>       <div class='boo'>         <div class='boo'></div>       </div>     </div>   </div> </div>

That’s real a pain to write manually. And the reason why this post was born was being horrified on seeing it generated with Haml like this:

.boo   .boo     .boo       .boo         .boo

There were actually about twenty levels of nesting in the code I saw, but maybe some people are reading thing on a mobile phone, so let’s not fill the entire viewport with boos, even if Halloween is near.

As you can probably tell, manually writing out every level is far from ideal, especially when the HTML is generated by a preprocessor (or from JavaScript, or even a back-end language like PHP). I’m personally not a fan of deep nesting and I don’t use it much myself, but if you’re going for it anyway, then I think it’s worth doing in a manner that scales well and is easily maintainable.

So let’s first take a look at some better solutions for this base case and variations on it and then see some fun stuff done with this kind of deep nesting!

The base solution

What we need here is a recursive approach. For example, with Haml, the following bit of code does the trick:

- def nest(cls, n); -  return '' unless n > 0; -  "<div class='#{cls}'>#{nest(cls, n - 1)}</div>"; end  = nest('👻', 5)

There’s an emoji class in there because we can and because this is just a fun little example. I definitely wouldn’t use emoji classes on an actual website, but in other situations, I like to have a bit of fun with the code I write.

We can also generate the HTML with Pug:

mixin nest(cls, n)   div(class=cls)     if --n       +nest(cls, n)  +nest('👻', 5)

Then there’s also the JavaScript option:

function nest(_parent, cls, n) {   let _el = document.createElement('div'); 	   if(--n) nest(_el, cls, n);    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

With PHP, we can use something like this:

<?php function nest($  cls, $  n) {   echo "<div class='$  cls'>";   if(--$  n > 0) nest($  cls, $  n);   echo "</div>"; }  nest('👻', 5); ?>

Note that the main difference between what each of these produce is related to formatting and white space. This means that targeting the innermost “boo” with .👻:empty is going to work for the Haml, JavaScript and PHP-generated HTML, but will fail for the Pug-generated one.

Adding level indicators

Let’s say we want each of our boos to have a level indicator as a custom property --i, which could then be used to give each of them a different background, for example.

You may be thinking that, if all we want is to change the hue, then we can do that with filter: hue-rotate() and do without level indicators. However, hue-rotate() doesn’t only affect the hue, but also the saturation and lightness. It also doesn’t provide the same level of control as using our own custom functions that depend on a level indicator, --i.

For example, this is something I used in a recent project in order to make background components smoothly change from level to level (the $ c values are polynomial coefficients):

--sq: calc(var(--i)*var(--i)); /* square */ --cb: calc(var(--sq)*var(--i)); /* cube */ --hue: calc(#{$  ch0} + #{$  ch1}*var(--i) + #{$  ch2}*var(--sq) + #{$  ch3}*var(--cb)); --sat: calc((#{$  cs0} + #{$  cs1}*var(--i) + #{$  cs2}*var(--sq) + #{$  cs3}*var(--cb))*1%); --lum: calc((#{$  cl0} + #{$  cl1}*var(--i) + #{$  cl2}*var(--sq) + #{$  cl3}*var(--cb))*1%);  background: hsl(var(--hue), var(--sat), var(--lum));

Tweaking the Pug to add level indicators looks as follows:

mixin nest(cls, n, i = 0)   div(class=cls style=`--i: $  {i}`)     if ++i < n       +nest(cls, n, i)  +nest('👻', 5)

The Haml version is not too different either:

- def nest(cls, n, i = 0); -   return '' unless i < n; -   "<div class='#{cls}' style='--i: #{i}'>#{nest(cls, n, i + 1)}</div>"; end  = nest('👻', 5)

With JavaScript, we have:

function nest(_parent, cls, n, i = 0) {   let _el = document.createElement('div');    _el.style.setProperty('--i', i); 	   if(++i < n) nest(_el, cls, n, i);    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

And with PHP, the code looks like this:

<?php function nest($  cls, $  n, $  i = 0) {   echo "<div class='$  cls' style='--i: $  i'>";   if(++$  i < $  n) nest($  cls, $  n, $  i);   echo "</div>"; }  nest('👻', 5); ?>

A more tree-like structure

Let’s say we want each of our boos to have two boo children, for a structure that looks like this:

.boo   .boo     .boo       .boo       .boo     .boo       .boo       .boo   .boo     .boo       .boo       .boo     .boo       .boo       .boo

Fortunately, we don’t have to change our base Pug mixin much to get this (demo):

mixin nest(cls, n)   div(class=cls)     if --n       +nest(cls, n)       +nest(cls, n)  +nest('👻', 5)

The same goes for the Haml version:

- def nest(cls, n); -   return '' unless n > 0; -   "<div class='#{cls}'>#{nest(cls, n - 1)}#{nest(cls, n - 1)}</div>"; end  = nest('👻', 5)

The JavaScript version requires a bit more effort, but not too much:

function nest(_parent, cls, n) {   let _el = document.createElement('div');      if(n > 1) {     nest(_el, cls, n);     nest(_el, cls, n)   }    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

With PHP, we only need to call the nest() function once more in the if block:

<?php function nest($  cls, $  n) {   echo "<div class='$  cls'>";   if(--$  n > 0) {     nest($  cls, $  n);     nest($  cls, $  n);   }   echo "</div>"; }  nest('👻', 5); ?>

Styling the top level element differently

We could of course add a special .top (or .root or anything similar) class only for the top level, but I prefer leaving this to the CSS:

:not(.👻) > .👻 {   /* Top-level styles*/ }

Watch out!

Some properties, such as transform, filter, clip-path, mask or opacity don’t only affect an element, but also also all of its descendants. Sometimes this is the desired effect and precisely the reason why nesting these elements is preferred to them being siblings.

However, other times it may not be what we want, and while it is possible to reverse the effects of transform and sometimes even filter, there’s nothing we can do about the others. We cannot, for example, set opacity: 1.25 on an element to compensate for its parent having opacity: .8.

Examples!

First off, we have this pure CSS dot loader I recently made for a CodePen challenge:

Here, the effects of the scaling transforms and of the animated rotations add up on the inner elements, as do the opacities.

Next up is this yin and yang dance, which uses the tree-like structure:

For every item, except the outermost one (:not(.☯️) > .☯️), the diameter is equal to half of that of its parent. For the innermost items (.☯️:empty, which I guess we can call the tree leaves), the background has two extra radial-gradient() layers. And just like the first demo, the effects of the animated rotations add up on the inner elements.

Another example would be these spinning candy tentacles:

Each of the concentric rings represents a level of nesting and combines the effects of the animated rotations from all of its ancestors with its own.

Finally, we have this triangular openings demo (note that it’s using individual transform properties like rotate and scale so the Experimental Web Platform features flag needs to be enabled in chrome://flags in order to see it working in Chromium browsers):

Triangular openings (live demo).

This uses a slightly modified version of the basic nesting mixin in order to also set a color on each level:

- let c = ['#b05574', '#f87e7b', '#fab87f', '#dcd1b4', '#5e9fa3']; - let n = c.length;  mixin nest(cls, n)   div(class=cls style=`color: $  {c[--n]}`)     if n       +nest(cls, n)  body(style=`background: $  {c[0]}`)   +nest('🔺', n)

What gets animated here are the individual transform properties scale and rotate. This is done so that we can set different timing functions for them.


The post Smarter Ways to Generate a Deep Nested HTML Structure appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,

Smarter Design Systems Tools

What has me really excited about building websites is largely around design systems and the design tools we use to build them. Though, design systems are certainly not limited to websites.

Closing the Gap

In the ever-so-hot-right-now world of design systems, one of the most common phrases people use is “bridging the gap” (between designers and engineers). This was on several résumés I reviewed during my time at Salesforce when we were hiring for the Lightning Design System team. It’s a fair endeavor to strive for, as we want to bring alignment, coherence, and unity to our product design and engineering — all with the goal of having a quality-consistent experience for the people using our products and services.

However, “bridging the gap” still implies there is a gap. Why is that gap there in the first place? Is it due to many years of legacy process and workflows that still seep into our day to day work? I could see legacy being a real-world scenario need to work through. But what about newer, smaller startups? Why do many of them have a gap, too?

As a design systems practitioner, advocate, and enthusiast, I am always seeking ways to improve the people side of our work: how we meet and communicate, how we prioritize our roadmap, and how we can iterate on our process and way of working. However, another piece of this are the tools we use.

Some of our tools have come quite a long way, and it certainly seems that a new one is announced pretty frequently these days. But the ones that really get me excited are where I see them do more than “bridge the gap”.

A while back, when Modulz arrived at the scene, I noticed they used the hashtag, #closethegap. I couldn’t help but love that.

I’m very keen to see our design tools move away from simply bridging gaps and more toward closing them. For example, a feature I see as only bridging the gap is a “developer handoff” (which tends to rely on pixel dimensions and hex codes delivered as a spec). In my experience, delivering specs like this can promote duplication in code, inconsistencies, and are prone to errors. An example of closing the gap, however, are tools that work with actual, real, quality code instead of vector boxes.

Some tools achieve this by exposing design tokens — you can see that in InVision DSM which has tokens built-in as a feature. Another tool, Diez, is an open-source developer toolkit using tokens to turn your design language into scalable code. There are other tools like the previously mentioned Modulz, along with Interplay, that work with coded components but in a visual way. This is much better than the earlier WYSIWYG editors of the past because their generated code can be accessible and production-quality. And that’s not to leave out the other players in this space like Adobe, Figma, Framer, Sketch, UXPin, and many, many more that are also doing a lot to move this forward.

The Invisible Design System

Something I’ve been thinking a lot about is how much time we spend on making beautiful design system websites. I love looking at them. They’re great. But as our design and engineering tools get closer and closer together, will we come to a point where we don’t need the website? Can our tools surface suggestions for better accessibility, localization, performance, and usability, because our design system is baked into the tools? Just a thought.

The No-Code Movement

Webflow, which empowers people to build websites (complete with business and e-commerce features) without writing code, recently had a conference called No Code Conf. According to their website, the conference celebrates the future of visual development.

This is a movement that, while not new, is growing rapidly. In addition to building a full-featured website using Webflow, visual, declarative app-building has been explored for years at larger tech giants like Salesforce (with their Lightning App Builder) to smaller companies like Glide which let you generate an entire app from a spreadsheet. And with all of the task-based automation services such as Zapier and IFTTT, there is much you can do without writing code at all.

Now don’t get me wrong. I still love writing code, and I love seeing other designers code. I enjoy working closely with engineers as well. I’m not saying we shouldn’t code at all. But I’m excited to see us move away from a slower “throw designs over a wall” process. I want to see people able to make things without the requirement to write code. It will also be nice to move away from the tired argument on whether designers should code — and we all move toward what can we make together?

And if we can get to a point where we don’t need to bridge or close any gaps because the gap never existed at all — well that’s a pretty exciting world I want to be in.

The post Smarter Design Systems Tools appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]