Tag: Thinking

Thinking Outside the Box with CSS Grid

Great tutorial from Alex Trost (based on some demos, like this one, from Andy Barefoot) showcasing how, while CSS grid has straight grid lines across and down, you can place items across grid lines creating a staggered effect that looks pretty rad. Grid-like, but it appears to align to diagonal lines rather than horizontal and vertical lines because of the staggering. And you still get all the flexibility of grid!

Direct Link to ArticlePermalink


The post Thinking Outside the Box with CSS Grid appeared first on CSS-Tricks.

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

CSS-Tricks

, ,

Thinking About Power Usage and Websites

Gerry McGovern asked if I had any insight into energy consumption and websites. He has a book, after all, about the digital costs on the planet. He was wondering about the specifics of web tech, like…

If you do this in HTML it will consume 3× energy but if you do it in JavaScript it will consume 10×.

I would think if you really looked, and knew exactly how to measure it, you could find examples like that. Say I wanted to move an element across the screen. If I wrote a setInterval loop in JavaScript that incremented the left position on the relative-positioned element by one millisecond, I’m 99% sure that takes more literal electricity to do than if you were to do a CSS @keyframes animation over the same time where you changed the transform: translateX() value. In that example, usually, we’re thinking about performance moreso than energy consumption, but that’s interesting right away: does good performance map to lower energy usage? Probably.

Researches have looked into this.

We discover a statistically significant negative correlation between performance scores and the energy consumption of mobile web apps (with medium to large effect sizes), implying that an increase of the performance score tend to lead to a decrease of energy consumption.

They were testing mobile web apps on Android using Lighthouse scores. I can guess that maps pretty well to other platforms and other performance metrics.

I’m glad the research so far maps to what I would logically expect to be true. Things that lead to poor performance are things that take energy. Imagine images. You’ll be dinged on performance scores for serving too large or unoptimized images. Imagine the performance implications of that. There are two images sitting on a server, a large one and a small one. Which one takes more electricity to travel to some user’s computer? The large one. Which one takes more processing power to parse and display? The large one. Which one occupies more memory (which uses electricity) for the duration of it’s life on screen? The large one.

The less across the network, the less electricity.

The less your browser has to do, the less electricity.

Some ad that auto-refreshes itself every few seconds? Not only is it annoying, but it’s bandwidth-wasteful and thus wasteful with electricity. Whenever you have to resort to polling (i.e. making a network request over and over) rather than something event-based like web sockets? That’s using electricity that you may not have needed to use.

We know that CDNs are good for performance too. Rather than a file (like an image) needing to travel across the world, it comes from a server much geographically closer on a server designed for that job. This is where things get a little more murky to me.

With performance as our goal: objective achieved. With low-energy consumption as our goal, are we there?

It has been studied, but unfortunately, I can’t tell what the conclusion is from the abstract alone. In my mind, things are complicated by the fact that servers around the world are storing copies of these assets, and when the assets change, it’s not just one server where they update, but again, servers around the world. There has got to be a balance between the propagation and duplicative storage as far as the savings that would be realized by the efficiency of saving requests.

Speaking of storage efficiency, I’m certain that storage just sitting on disk takes a lot less electricity than files being sent over networks — but it still has a cost. Say you saved a copy of every file every time you changed it. Say you saved a complete copy of your website every time you deployed it. Useful? Sure. Does that cost electricity? It must. There must be some balance to strike there.

Gerry was asking me about particular technologies though. I can think of another big deal thing in CSS land: dark mode! Yet again, it’s been studied. Dark mode saves power.

Dark Mode can indeed reduce the display power draw by up to 58.5% at full brightness for the set of popular Android apps that we tested! In terms of whole phone battery drain reduction, that translates into 5.6% to 44.7% savings at full brightness and 1.8% to 23.5% savings at 38% brightness.

And what about comparing technologies? I suspect it’s far more about what that technology (or language) is doing than the language itself. For example, I can build a little area that opens and closes in HTML with a <details> element. Is that more energy-efficient than creating that area by attaching a click handler on a button that toggles the class of an element that visually opens and closes it? I kinda doubt it. I’d bet the electricity being used in the re-paint/re-render steps that the browser is doing and the languages behind it are less relevant. And yet! If I made the browser download a 50 KB JavaScript library just to implement my little open/close element, then yes, it does matter, and the JavaScript version is less efficient.

In that way, just as good performance generally maps to less energy consumption, I’d bet that adhering to the rule of least power generally maps to less energy consumption as well.

Sick of me guessing at stuff? Fair enough.

Jack Lenox’s article “How Improving Website Performance Can Help Save The Planet” on Smashing is a better deep dive. He points to websites that will test your site. Website Carbon Calculator is one example and it states:

Calculating the carbon emissions of website is somewhat of a challenge, but using five key pieces of data we can make a pretty good estimate:

1. Data transfer over the wire
2. Energy intensity of web data
3. Energy source used by the data centre
4. Carbon intensity of electricity
5. Website traffic

The testing code is open source.


The post Thinking About Power Usage and Websites appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

Thinking in Behaviors, Not Screen Sizes

Chase McCoy wrote a nifty post about the “gap problem” when making a grid of items. His argument might be summarized like this: how should we space elements with margins in CSS? He notes that the gap property isn’t quite ready for prime time when it comes to using it with flexbox, like this:

.grid {   display: flex;   gap: 10px; }

Right now, using gap with flexbox is only supported in Firefox and I’ve already caught myself forgetting about that in a few projects. So watch out for that.

Anyway, the part about Chase’s blog post that I love is where he mentions Andy Bell’s technique for creating a responsive layout with no media queries, like this:

.grid {   display: grid;   grid-gap: 10px;   grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

This CSS is doing the following:

  • Make a grid with a 10px gap between each column and row.
  • Each column should have a minimum width (150px).
  • Each column should also be equal width (1fr).
  • The grid should auto-fill as many columns that can fit.

The nifty thing about all this is that our grid is now effectively responsive because of minmax — if you resize the browser, then the grid will snap down into fewer columns, just like this:

No media queries at all! Although sure, there’s a few other ways that you could get this to work but I think this is neat not just because we’re avoiding media queries — instead, it’s because it teaches us to think in a new way when designing and building components.

Chase continues:

With this technique, instead of using breakpoints to specify the screen size where your items should stack, you specify the minimum size an element should be before it stacks. I like this because it encourages developers to think about responsive design in terms of behaviors instead of screen sizes.

“Behaviors instead of screen sizes” is such a great way to think about component design! A lot of the problems I’ve encountered when making components for a design system is when I’ve been thinking about screen sizes — mobile, tablet, desktop, etc. — and trying to make those components fit within those constraints.

Thinking in behaviors is always more effective because there are so many things that can impact a component beyond what screen or device width we’re working with. Perhaps we want that component to fit inside another component. Or we want to align some helper text to the side of it for comparison.

Either way, thinking about behaviors instead of screen sizes isn’t really going to be fully impossible until we have container queries, as Chris writes:

Container queries are always on the top of the list of requested improvements to CSS. The general sentiment is that if we had container queries, we wouldn’t write as many global media queries based on page size. That’s because we’re actually trying to control a more scoped container, and the only reason we use media queries for that now is because it’s the best tool we have in CSS. I absolutely believe that.

The post Thinking in Behaviors, Not Screen Sizes appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Thinking Through Styling Options for Web Components

Where do you put styles in web components?

I’m assuming that we’re using the Shadow DOM here as, to me, that’s one of the big draws of a web component: a platform thing that is a uniquely powerful thing the platform can do. So this is about defining styles for a web component in a don’t-leak-out way, and less so a way to get global styles to leak in (although that’s very interesting as well, which can be done via custom properties which we’ll look at later in the article).

If you’re building the template inside the JavaScript — which is nice because of template literals and how we can sprinkle our data into the template nicely — you need access to those styles in JavaScript.

const template = `   <style>$  {styles}</style>   <div class="$  {class}">     <h2>$  Thinking Through Styling Options for Web Components</h2>     $  {content}   </div> `;

Where does that style variable come from? Maybe also a template literal?

const style = `   :host {     background: white;   }   h2 {     font: 900 1.5rem/1.1 -system-ui, sans-serif;   } `;

I guess that’s fine, but it makes for a big messy block of code just dunked somewhere in the class where you’re trying to build this web component.

Another way is to <template> the template and make a <style> block part of it.

<template id="card-template">   <style>     :host {       background: white;     }     h2 {       font: 900 1.5rem/1.1 -system-ui, sans-serif;     }   </style>    <div id="card-hook">     <h2 id="title-hook"></h2>     <p id="desc-hook"></p>   </div> </template>

I can see the appeal with this because it keeps HTML in HTML. What I don’t love about it is that you have to do a bunch of manual shadowRoot.querySelector("#title-hook").innerHTML = myData.title; work in order to flesh out that template. That doesn’t feel like a convenient template. I also don’t love that you need to just chuck this template somewhere in your HTML. Where? I dunno. Just chuck it in there. Chuck it.

The CSS is moved out of the JavaScript too, but it just moved from one awkward location to another.

If we wanted to keep the CSS in a CSS file, we can sorta do that like this:

<template id="card-template">   <style>     @import "/css/components/card.css";   </style>    <div id="card-hook">     <h2 id="title-hook"></h2>     <p id="desc-hook"></p>   </div> </template>

(The use of <link rel="import" type="css" href=""> is deprecated, apparently.)

Now we have @import which is an extra HTTP Request, and notorious for being a performance hit. An article by Steven Lambert says it clocked in at half a second slower. Not ideal. I don’t suppose it would be much better to do this instead:

class MyComponent extends HTMLElement {        constructor() {     super();     this.attachShadow({ mode: "open" });      fetch('/css/components/card.css')       .then(response => response.text())       .then(data => {         let node = document.createElement('style');         node.innerHTML = data;         document.body.appendChild(node);       });   }    // ... }

Seems like that would potentially be a Flash-of-Unstyled-Web-Component? I guess I should get off my butt and test it.

Now that I’m digging into this again, it seems like ::part has gotten some steam (explainer). So I can do…

const template = `   <div part="card">     <h2>$  Thinking Through Styling Options for Web Components</h2>     $  {content}   </div> `;

…then write styles in a global stylesheet that only apply inside that Shadow DOM, like:

my-card::part(card) {   background: black;   color: white; }

…which has a smidge of browser support, but maybe not enough?

These “part” selectors can only touch the exact element it’s connected to. You’d have to do all your styling by applying a part name to every single DOM node and then styling each entirely on its own. That’s no fun, particularly because the appeal of the Shadow DOM is this isolated styling environment in which we’re supposed to be able to write looser CSS selectors and not be worried our h2 { } style is going to leak all over the place.

Looks like if native CSS modules becomes a thing, that will be the most helpful thing that could happen.

import styles from './styles.css';  class MyElement extends HTMLElement {   constructor() {     this.attachShadow({mode: open});     this.shadowRoot.adoptedStyleSheets = [styles];   } }

I’m not sure, however, if this is any sort of performance boost. Seems like it would be a wash between this and @import. I have to say I prefer the clarity and syntax with native CSS modules. It’s nice to be writing JavaScript when working with JavaScript.

Constructable Stylesheets also look helpful for sharing a stylesheet across multiple components. But the CSS modules approach looks like it could also do that since the stylesheet has already become a variable at that point.

The post Thinking Through Styling Options for Web Components appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Thinking in React Hooks

Amelia Wattenberger has written this wonderful and interactive piece about React Hooks and details how they can clean up code and remove all those troubling lifecycle events:

React introduced hooks one year ago, and they’ve been a game-changer for a lot of developers. There are tons of how-to introduction resources out there, but I want to talk about the fundamental mindset change when switching from React class components to function components + hooks.

Make sure you check out that lovely animation when you select the code, too. It’s a pretty smart effect to show the old way of doing things versus the new and fancy way. Also make sure to check out our very own Intro to React Hooks once you’re finished to find more examples of this pattern in use.

Direct Link to ArticlePermalink

The post Thinking in React Hooks appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

The Thinking Behind Simplifying Event Handlers

Events are used to respond when a user clicks somewhere, focuses on a link with their keyboard, and changes the text in a form. When I first started learning JavaScript, I wrote complicated event listeners. More recently, I’ve learned how to reduce both the amount of code I write and the number of listeners I need.

Let’s start with a simple example: a few draggable boxes. We want to show the user which colored box they dragged.

<section>   <div id="red" draggable="true">     <span>R</span>   </div>   <div id="yellow" draggable="true">     <span>Y</span>   </div>   <div id="green" draggable="true">     <span>G</span>   </div> </section>  <p id="dragged">Drag a box</p>

See the Pen
Dragstart events
by Tiger Oakes (@NotWoods)
on CodePen.

The intuitive way to do it

I wrote separate event listener functions for each element when I first started learning about JavaScript events. It’s a common pattern because it’s the simplest way to start. We want specific behavior for each element, so we can use specific code for each.

document.querySelector('#red').addEventListener('dragstart', evt => {   document.querySelector('#dragged').textContent = 'Dragged red'; });  document.querySelector('#yellow').addEventListener('dragstart', evt => {   document.querySelector('#dragged').textContent = 'Dragged yellow'; });  document.querySelector('#green').addEventListener('dragstart', evt => {   document.querySelector('#dragged').textContent = 'Dragged green'; });

Reducing duplicate code

The event listeners in that example are all very similar: each function displays some text. This duplicate code can be collapsed into a helper function.

function preview(color) {   document.querySelector('#dragged').textContent = `Dragged $  {color}`; }  document   .querySelector('#red')   .addEventListener('dragstart', evt => preview('red')); document   .querySelector('#yellow')   .addEventListener('dragstart', evt => preview('yellow')); document   .querySelector('#green')   .addEventListener('dragstart', evt => preview('green'));

This is much cleaner, but it still requires multiple functions and event listeners.

Taking advantage of the Event object

The Event object is the key to simplifying listeners. When an event listener is called, it also sends an Event object as the first argument. This object has some data to describe the event that occurred, such as the time the event happened. To simplify our code, we can use the evt.currentTarget property where currentTarget refers to the element that the event listener is attached to. In our example, it will be one of the three colored boxes.

const preview = evt => {   const color = evt.currentTarget.id;   document.querySelector('#dragged').textContent = `Dragged $  {color}`; };  document.querySelector('#red').addEventListener('dragstart', preview); document.querySelector('#yellow').addEventListener('dragstart', preview); document.querySelector('#green').addEventListener('dragstart', preview);

Now there is only one function instead of four. We can re-use the exact same function as an event listener and evt.currentTarget.href will have a different value depending on the element that fires the event.

Using bubbling

One final change is to reduce the number of lines in our code. Rather than attaching an event listener to each box, we can attach a single event listener to the <section> element that contains all the colored boxes.

An event starts off at the element where the event originated (one of the boxes) when it is fired. However, it won’t stop there. The browser goes to each parent of that element, calling any event listeners on them This will continue until the root of the document is reached (the <body> tag in HTML). This process is called “bubbling” because the event rises through the document tree like a bubble.

Attaching an event listener to the section will cause the focus event to bubble from the colored box that was dragged up to the parent element. We can also take advantage of the evt.target property, which contains the element that fired the event (one of the boxes) rather than the element that the event listener is attached to (the <section> element).

const preview = evt => {   const color = evt.target.id;   document.querySelector('#dragged').textContent = `Dragged $  {color}`; };  document.querySelector('section').addEventListener('dragstart', preview);

Now we’ve reduced many event listeners to just one! With more complicated code, the effect will be greater. By utilizing the Event object and bubbling, we can tame JavaScript events and simplify code for event handlers.

What about click events?

evt.target works great with events like dragstart and change, where there are only a small number of elements that can receive focus or have input changed.

However, we usually want to listen for click events so we can respond to a user clicking on a button in an application. click events fire for any element in the document, from large divs to small spans.

Let’s take our draggable color boxes and make them clickable instead.

<section>   <div id="red" draggable="true">     <span>R</span>   </div>   <div id="yellow" draggable="true">     <span>Y</span>   </div>   <div id="green" draggable="true">     <span>G</span>   </div> </section>  <p id="clicked">Clicked a box</p>
const preview = evt => {   const color = evt.target.id;   document.querySelector('#clicked').textContent = `Clicked $  {color}`; };  document.querySelector('section').addEventListener('click', preview);

See the Pen
Click events: Not quite working
by Tiger Oakes (@NotWoods)
on CodePen.

When testing this code, notice that sometimes nothing is appended to “Clicked” instead of when clicking on a box. The reason that it doesn’t work is that each box contains a <span> element that can be clicked instead of the draggable <div> element. Since the spans don’t have a set ID, the evt.target.id property is an empty string.

We only care about the colored boxes in our code. If we click somewhere inside a box, we need to find the parent box element. We can use element.closest() to find the parent closest to the clicked element.

const preview = evt => {   const element = evt.target.closest('div[draggable]');   if (element != null) {     const color = element.id;     document.querySelector('#clicked').textContent = `Clicked $  {color}`;   } };

See the Pen
Click events: Using .closest
by Tiger Oakes (@NotWoods)
on CodePen.

Now we can use a single listener for click events! If element.closest() returns null, that means the user clicked somewhere outside of a colored box and we should ignore the event.

More examples

Here are some additional examples to demonstrate how to take advantage of a single event listener.

Lists

A common pattern is to have a list of items that can be interacted with, where new items are inserted dynamically with JavaScript. If we have event listeners attached to each item, then y\our code has to deal with event listeners every time a new element is generated.

<div id="buttons-container"></div> <button id="add">Add new button</button>
let buttonCounter = 0; document.querySelector('#add').addEventListener('click', evt => {   const newButton = document.createElement('button');   newButton.dataset.number = buttonCounter;      // Make a new event listener every time "Add new button" is clicked   newButton.addEventListener('click', evt => {      // When clicked, log the clicked button's number.     document.querySelector('#clicked').textContent = `Clicked button #$  {newButton.textContent}`;   });    buttonCounter++;    const container = document.querySelector('#buttons-container');   container.appendChild(newButton); });

See the Pen
Lists: no bubbling
by Tiger Oakes (@NotWoods)
on CodePen.

By taking advantage of bubbling, we can have a single event listener on the container. If we create many elements in the app, this reduces the number of listeners from n to two.

let buttonCounter = 0; const container = document.querySelector('#buttons-container'); document.querySelector('#add').addEventListener('click', evt => {   const newButton = document.createElement('button');   newButton.dataset.number = buttonCounter;   buttonCounter++;    container.appendChild(newButton); }); container.addEventListener('click', evt => {   const clickedButton = evt.target.closest('button');   if (clickedButton != null) {     // When clicked, log the clicked button's number.     document.querySelector('#clicked').textContent = `Clicked button #$  {clickedButton.dataset.number}`;   } });

Forms

Perhaps there’s a form with lots of inputs, and we want to collect all the user responses into a single object.

<form>   <label>Name: <input name="name" type="text"/></label>   <label>Email: <input name="email" type="email"/></label>   <label>Password: <input name="password" type="password"/></label> </form> <p id="preview"></p>
let responses = {   name: '',   email: '',   password: '' };  document   .querySelector('input[name="name"]')   .addEventListener('change', evt => {     const inputElement = document.querySelector('input[name="name"]');     responses.name = inputElement.value;     document.querySelector('#preview').textContent = JSON.stringify(responses);   }); document   .querySelector('input[name="email"]')   .addEventListener('change', evt => {     const inputElement = document.querySelector('input[name="email"]');     responses.email = inputElement.value;     document.querySelector('#preview').textContent = JSON.stringify(responses);   }); document   .querySelector('input[name="password"]')   .addEventListener('change', evt => {     const inputElement = document.querySelector('input[name="password"]');     responses.password = inputElement.value;     document.querySelector('#preview').textContent = JSON.stringify(responses);   });

See the Pen
Forms: no bubbling
by Tiger Oakes (@NotWoods)
on CodePen.

Let’s switch to a single listener on the parent <form> element instead.

let responses = {   name: '',   email: '',   password: '' };  document.querySelector('form').addEventListener('change', evt => {   responses[evt.target.name] = evt.target.value;   document.querySelector('#preview').textContent = JSON.stringify(responses); });

Conclusion

Now we know how to take advantage of event bubbling and the event object to simplify complex jumbles of event handlers into just a few… and sometimes down to just one! Hopefully this article has helped you think about your event handlers in a new light. I know this was a revelation to me after I’d spent my early development years writing duplicative code to accomplish the same thing.

The post The Thinking Behind Simplifying Event Handlers appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]