Developer News

How Well Do You Know CSS Layout?

Css Tricks - Tue, 01/15/2019 - 4:54am

The difference between a CSS good experience and a long frustrating one is oftentimes a matter of a few small details. CSS is indeed nuanced. One of the most common areas where I see struggles is layout. Personally, I like to study patterns. I notice that I tend to use a small group of patterns to solve the majority of my layout problems. This article is about those CSS patterns I use to get myself through layout challenges. It is also about approaching situations agnostically, regardless of the CSS methodologies used, whether that’s SMACSS, BEM, or even the hot topic of CSS-in-JS because they all focus on the properties themselves rather than architecture, organization, or strategy.

Just for fun, let’s start with a test

We’ll use a platform that I happen to have made called Questionable.io and I’ve used it to create a test that we’ll get to below. Don’t worry, there is no personal data collected, results are anonymous and it’s totally free.

The purpose of the test is to see if you can recognize specific CSS behaviors and problems in context without first being presented with the material. I didn’t set out to make the test difficult, but CSS layout nuances tend to be somewhat complex, especially without having a lot of exposure to them. Remember, this all for fun. The results are not an indication of your awesomeness, but hopefully you get value out of it.

The test is 10 questions and should take 10 minutes or less.

Take CSS Layout Quiz

Interested in the test but don’t want to take it? Here’s a link to the questions with their correct answers.

Done already? Great! Let’s go over the questions one-by-one to get a better understanding of the layout patterns that are covered in the test.

Question 1: Box Model

Learning the Box Model should be high priority on anyone’s list. While this CSS-Tricks Box Model Article may be a bit old, don’t underestimate its value and relevance to modern CSS. The Box Model is prerequisite knowledge for almost every CSS topic related to layout.

This particular question is testing how to get the Box Model’s computed width. The box clearly has width: 100px; but it turns out that the default rules of the Box Model apply width properties to the content layer of the box. The computed width (how wide is rendered on the page) is the sum of the content layer, padding layer, and border layer. For this reason, the answer is 112px:

.box { width: 100px; /* Take this */ height: 50px; padding: 5px; /* Plus this x2 for left and right */ border: 1px solid red; /* Plus this x2 for left and right */ background-color: red; /* = 112px of computed width */ }

If you’ve encountered a situation where the last column or tab in a UI wraps down to the next line and you were confident that five tabs (all set to width: 20%;) adds up to 100%, then it’s very possible that this was the issue. Five tabs at 20% width does add up to 100%, but if there’s padding and/or borders involved, those will add width there won’t be room for the last tab to fit on the same line.

Around the time of CSS3 being introduced, a new tool called box-sizing came to CSS. This allows us to change what layer of the Box Model we want width to apply. For example, we can do box-sizing: border-box; which means we want any width rules to apply to the outside of the border layer instead of the content layer. In this test question, if box-sizing: border-box; had been applied, the computed width would have been 100px.

This is old news for some of you but a good reminder for pros and novices alike.

There are a number of articles on the Box Model and how to use box-sizing as a reset, so it’s applied to your entire project all at once. Box Sizing and Inheriting box-sizing Probably Slightly Better Best-Practice are two great articles right on CSS-Tricks to get started.

Question 2: Borders are pushy

The second test question could almost be considered "Part Two" of the first question. Remember, it’s one thing to read, "The Box Model has layers and they all contribute to the calculated width and hight." It’s another to be able to recognize a Box Model problem in a real situation. This particular problem is somewhat of a classic among those who have been doing CSS for a while. It stems from the fact that borders take up space and will push things around since they are a part of the Box Model. Introducing borders during a state-transition, like :hover, will mean that boxes get bigger and thus push subsequent boxes down. It can also create a jittery experience:

See the Pen CSS-Tricks: Borders are Dimension by Brad Westfall (@bradwestfall) on CodePen.

Out of all the possible solutions in the test question, doing border: 2px solid transparent on the initial "un-hovered" state would be the only one that fixes the problem. box-sizing doesn’t fix this problem because we are not explicitly setting a height. If we had, then the border would be factored on the inside of the height and there would be no shift — but this wasn’t the case.

There are also other solutions that weren’t mentioned as possible answers. One is faux borders with box-shadow and the other is to use outline instead of border. Either of those would have resulting in no shifting during state changes because they are not layers in the Box Model. Here’s another CSS-Tricks article to read more about these solutions

Keep in mind that outline does not support border-radius.

Question 3: Absolute position vs. fixed position

Aside from knowing when to use each and how they differ in visual behavior, it’s also very important to know the rules for how each positioning method attaches to a parent element with its top, right, bottom, or left properties.

First, let’s review Containing Block. The short definition is that a Containing Block is most often the parent of any given element. However, the rules for Containing Block are different between absolute and fixed elements:

1. For absolute elements: The Containing Block is the nearest ancestor parent that is not static. For example, when an element is absolute-positioned, and contains top, right, bottom, or left properties, it will position relative to any parent that has a position of absolute, relative, fixed, or sticky.
2. For fixed elements: The Containing Block is the viewport, regardless of any parents that have position values other than static. Also, the scrolling behavior is different than absolute in that position: fixed; elements stay "fixed" to the viewport as it scrolls, hence the name.

Many developers believe absolute-positioned elements only seek the nearest position: relative; parent. This is a common misconception simply because position: relative is most often paired with position: absolute; to make a Containing Block. The reason it’s commonly used is because relative keeps the parent in flow which is often the desirable behavior. There are times though that the Containing Block of an absolute positioned element is also absolute positioned. This is totally okay depending on the situation. If all parents are static, then the absolute positioned element will attach to the viewport — but in a way that scrolls with the viewport:

See the Pen CSS-Tricks: Position Absolute Scrolling by Brad Westfall (@bradwestfall) on CodePen.

There is a lesser-known caveat to the two rules above: Anytime a parent has a transform property (among a few others) with a value other than none, then that parent will become the Containing Block for absolute- and fixed-positioned elements. This can be observed in this Pen where the notice is position: fixed; and the parent has transform but only when hovered:

See the Pen CSS-Tricks: Containing Blocks by Brad Westfall (@bradwestfall) on CodePen.

Question 4: Parent and first/last child collapsing margins

This is one of those CSS details that can really bite you if you don’t know how it works. There is a CSS concept called Collapsing Margins and many people are familiar with the form of it called Adjacent Siblings Collapsing Margins. However, there is another form of it called Parent and First/Last Child Collapsing Margins which is lesser known. Here is a demo of both:

See the Pen CSS-Tricks: Collapsing Margins by Brad Westfall (@bradwestfall) on CodePen.

Each paragraph tag has a top and bottom margin of 1em that are provided by the browser. So far, that’s the easy part. But why is the gap between the paragraphs not 2em (the sum of the top and bottom)? This is called Adjacent Sibling Collapsing Margins. The margins overlap such that the larger of the two margins will be the total gap size, thus the gap in this case is 1em.

There’s something else happening that’s a little strange though. Did you notice that the top margin of the first paragraph doesn’t create a gap between it and the blue container div? Instead of a gap, it’s almost like it "contributes" the margin to the parent div as if the div had the top margin. This is called Parent and First/Last Child Collapsing Margins. This form of Collapsing Margins will not happen in some circumstances if the parent has any of these:

  • Top/Bottom padding of any value bigger than 0.
  • Top/Bottom border of any width bigger than 0.
  • Block Formatting Context, which can be created by things like overflow: hidden; and overflow: auto;).
  • display: flow-root (not well supported).

When I have the pleasure of explaining this small CSS detail to people and solving it with padding or border, the response is almost always, "what about padding or border of 0?" Well, that doesn’t work because the value must be a positive integer.

In the previous example, just 1px of padding allows us to toggle between using and preventing Parent/Child Collapsing Margins. The gap that shows up between the first/last paragraphs and the parent is the 1px of padding but now the margin is being factored to the inside of the container since the padding layer creates a barrier preventing collapsing margins.

Regarding the question, I’m confident you can see what the problem is in this UI:

See the Pen CSS-Tricks: Parent/Child Collapsing Margins by Brad Westfall (@bradwestfall) on CodePen.

The first .comment (without the .moderator class) is experiencing Collapsing Margins. Even without looking at the code, we can see that the moderator comment has a border and the non-moderator one does not. In the question, there were actually three answers that were considered correct. Each one is actually already applied in the source of the Pen, they're just commented out.

One reason why this form of Collapsing Margins isn’t as widely known as the others is the wide array of ways we can "accidentally" avoid it. Flexbox and grid items create a Block Formatting Context, so we don’t see this form of Collapsing Margins there. If our "comments" UI were a real project, chances are we would have had padding on all four coordinates to create spacing all the way around, which would fix any Collapsing Margins for us. As rare as it might be, I wouldn’t want you to spend a whole day scratching you head on this one, so it’s good to keep in your thoughts when working with layout.

Here are some CSS-Tricks articles on this subject:

Question 5: Percent of what?

When it comes to using percentage units, the percent is said to be based on the Containing Block’s width or height (usually related to the parent). As we stated earlier, an element with transform will become a Containing Block, so when an element is using transform, the percentage units (for transform only) are based on its own size rather than the parent.

In this example, we can see that 50% means two different things depending on context. The first red block has margin-left: 50%; and the second red block is using transform: translateX(50%);:

See the Pen CSS-Tricks - Percentage and Transform by Brad Westfall (@bradwestfall) on CodePen.

Question 6: The Box Model strikes again... what a hangover!

Just when you thought we were done talking about Box Model...

See the Pen CSS-Tricks: Left: 0 Right: 0 by Brad Westfall (@bradwestfall) on CodePen.

The hangover stems from the fact that we are using width: 100%; on the footer and also adding padding. The container is 500px wide which means the footer's content layer (being 100%) is 500px wide before padding is applied to the outside of that layer.

The hangover can be fixed with one of these two common techniques:

  1. Use box-sizing on the footer directly or via a reset, like we discussed earlier.
  2. Remove the width and do left: 0; right: 0; instead. This is a great use case for doing a left value and a right value at the same time. Doing so will avoid Box Model issues because the width will use its default value auto to take up any available space between paddings and borders when left: 0; right: 0; are set.

One of the options was "Remove the padding on the footer." This would technically work to fix the hangover because the content layer being 100% would have no padding or border to expand it beyond the width of the container. But I think this solution is the wrong approach because we shouldn’t have to change our UI to accommodate Box Model issues that are easily avoided.

The reality for me is that I always have box-sizing: border-box; as apart of my reset. If you also do this, then perhaps you don’t see this problem often. But I still like to do the left: 0; right: 0; trick anyways because, over time, it has been more stable (at least in my experience) than having to deal with Box Model issues arising from width: 100%; on positioned elements.

Question 7: Centering absolute and fixed elements

Now we’re really starting to combine all the material from above with the centering of absolute and fixed elements:

See the Pen CSS-Tricks: Modal (Lightbox) Centering by Brad Westfall (@bradwestfall) on CodePen.

Since we’ve already covered most of the material in this test question, I’ll simply point out that horizontal and vertical centering can be done "the old school way" with negative margins or the newer "kinda old school but still good" way of doing transforms. Here is an amazing CSS-Tricks guide on all things centering.

It used to be said that if we know the width and height of the box, then we should use negative margins because they’re more stable than transitions, which were new to browsers. Now that transitions are stable, I use them almost all the time for this, unless I need to avoid a Containing Block.

Also know that we can’t use any margin: auto; tricks for this because we need modals to "hover" over the content which is why position is typically used to them out of Normal Flow.

Speaking of which, let’s move on to the next question, which deals with centering with Normal Flow.

Question 8: Centering elements with Normal Flow

Flexbox brought us many amazing tools for solving difficult layout problems. Before it’s release, it was said that vertical centering was one of the most difficult things to do in CSS. Now it’s somewhat trivial:

.parent { display: flex; } .child { margin: auto; }

See the Pen CSS-Tricks: Flexbox Centering (Vertical and Horizontal) by Brad Westfall (@bradwestfall) on CodePen.

Notice that with flexbox items, the margin: auto is being applied to top, right, bottom, and left to center vertically and horizontally. Doing vertical centering with auto didn't work in the past with block-level elements which is why doing margin: 0 auto is common.

Question 9: Calculate mixed units

Using calc() is perfect when two units that we can’t add up on our own need to be mixed or when we need to make fractions easier to read. This test question asks us to figure out what calc(100% + 1em) would be based on the fact that the width of the div is 100px. This was a little tricky because it actually doesn't matter that the div is 100px wide. The percent is based on the parent's width so the answer is Whatever 100% of the containing block's (parent's) width is plus 1em.

There are a few key places where I see myself regularly reaching for calc(). One is anytime I want to offset something by 100% but also add a fixed amount of extra space. Dropdown menus can be a good example of this:

See the Pen CSS Tricks: Calculate Mixed Units by Brad Westfall (@bradwestfall) on CodePen.

The trick here is that we want to make a "dropdown system" where the dropdown menu can be used with different trigger sizes (in this case, two different size buttons). We don’t know what the height of the trigger will be but we do know that top: 100%; will placed at the top of our menu and at the very bottom of the trigger. If every menu needs to be at the bottom of their respective trigger, plus .5em, then that can happen with top: calc(100% + 0.5em);. Sure, we could use top: 110%; as well, but that extra 10% would be context-dependent based on the height of the trigger and the container.

Question 10: Negative margins

Unlike positive margins that push away from their siblings, negative margins pull them closer together without moving the sibling elements. This final test question offers two solutions that technically work to eliminate the double border in our button group, but I strongly prefer the negative margins technique because removing borders would make it much more challenging to do certain tricks like this hover effect:

See the Pen CSS Tricks: Negative Margins with Button Groups by Brad Westfall (@bradwestfall) on CodePen.

The effect is a "common border" that is shown between the buttons. Buttons can't actually share a common border so we need this negative margin trick to make the two borders overlap. Then I'm using a z-index to manage which border I want to be on top depending on the hover state. Note that z-index is useful here even without absolute positioning, but I did have to do position: relative. If I had used the technique to remove the left border of the second button, this effect would have been more difficult to pull off.

It all adds up!

There is one last demo I want to show you that utilizes many tricks we’ve discussed so far. The task is to create UI tiles that expand all the way to the left and right edges of the container with gutters. By tiles, I mean the ability to have a list of blocks that wraps down to the next line when there’s no more space. Hover over the tiles to see the full effect:

See the Pen CSS-Tricks: Flexbox Tiles (Edge-to-Edge) by Brad Westfall (@bradwestfall) on CodePen.

The hurdle with this task is the gutters. Without gutters, it would be trivial to get the tiles to touch the left and right edge of the container. The problem is that the gutters will be created by margin, and when we add margin to all sides of the tile, we create two problems:

  1. Having three tiles with width: 33.33%; in combination with margin will mean three tiles cannot fit on one row. While box-sizing will allow us to have padding and borders on the .tile which will be contained within 33.33%, it will not help us with margins — that means the computed width of the three tiles will be more than 100%, forcing the last one down to the next line.
  2. Tiles on the far left and right side will no longer touch the edges of the container.

The first problem can be solved with calc((100% / 3) - 1em). That’s 33.33% minus the left and right margins of each tile. Adjacent Sibling Collapsing Margins don’t apply here because there is no such thing as Collapsing Margins when it comes to left and right margin. As a result, the horizontal distance between each tile is the sum of the two margins (1em). It also doesn’t apply in this case with the top and bottom margins because the first tile and the fourth tile are technically not Adjacent Siblings, even though they happen to be right next to each other visually.

With the calc() trick, three tiles are able to fit on a row, but they still don’t extend to edges of the container. For that, we can use negative margins in the amount equal to the left and right margin of each tile. The green dotted line in the example is the container where we will apply negative margins to draw out the tiles to match the edge of the surrounding content. We can see that how it extends into its parent’s padding area (the main element). That’s okay because negative margins don’t push neighboring elements around.

The end result is that the tiles have nice gutters that extend edge-to-edge so that they align to the neighboring paragraph tags outside the tiles.

There’s a lot of ways to solve tiles (and they usually come with their own pros and cons). For example, there's a rather elegant solution using CSS Grid discussed by Heydon Pickering which is responsive using a technique that mimics container queries (but with Grid magic). Ultimately, his Grid solution to tiles is much nicer than the flexbox solution I presented, but it also has less browser support. Nonetheless, the flexbox solution is still a great way to demo all the tricks from this article at the same time.

You may already be familiar with Heydon's work. He's known for creating clever tricks like the Lobotomized Owl selector. If you're not familiar, it's certainly worth knowing and I have a video where I talk about it.

Summary

I stated at the start that I tend to look for patterns when solving problems. This article isn’t necessarily about the exact demo scenarios from above; it’s more about a set of tools that can be used to solve these and many other layout problems that we are all likely to come across. I hope these tools take you far and I look forward to hearing your contributions in the comments.

By the way, there are a number of excellent resources that cover the Box Model in thorough detail, most notably ones by Rachel Andrews and Jen Simmons that are certainly worth checking out. Rachel even has a newsletter completely dedicated to layout.

  • Box Alignment Cheatsheet - Great resource with visuals that highlight the various properties that affect how elements are aligned, either by themselves or relative to other elements.
  • Jen Simmons Labs - A slew of helpful posts, demos and experiments using modern layout methods.

The post How Well Do You Know CSS Layout? appeared first on CSS-Tricks.

CSS doesn’t suck

Css Tricks - Tue, 01/15/2019 - 4:50am

I'm not so protective of CSS that I'm above hearing it criticized, but I'm certainly in agreement here. CSS does not suck. I love how the post is framed to hype up current CSS features the way features of other languages and tools are hyped:

Imagine if a tech dude walked on stage at a conference and said the following:

“This declarative language will gracefully continue on failure, allow you to write global and scoped code, and it will work across your entire front-end stack, whether it’s rendered by a framework, a CMS or a static HTML file”

... Now, if I make a slight amendment to that, the reception would probably be the exact opposite.

“CSS will gracefully continue on failure, allow you to write global and scoped code, and it will work across your entire front-end stack, whether it’s rendered by a framework, a CMS or a static HTML file”

Direct Link to ArticlePermalink

The post CSS doesn’t suck appeared first on CSS-Tricks.

In Defense of Utility-First CSS

Css Tricks - Tue, 01/15/2019 - 4:48am

A rather full-throated argument (or rather, response to arguments against) utility (atomic) CSS from Sarah Dayan. I wondered recently if redesigns were potentially a weakness of these types of systems (an awful lot of tearing down classes) which Sarah acknowledges and recommends more abstraction to help.

I also wonder about workflow. I sort of demand working in an environment which offers style injection, so working with CSS feels smooth. I also worry that having to change HTML every time I want to modify a design requires refreshing. I guess if you are in a hot module reloading situation, then it's fine.

Also this seems like it can just go too far. At some point, altering a space-separated string to do everything you wanna do has ergonomic limitations.

Direct Link to ArticlePermalink

The post In Defense of Utility-First CSS appeared first on CSS-Tricks.

Using React Portals to Render Children Outside the DOM Hierarchy

Css Tricks - Mon, 01/14/2019 - 1:22pm

Say we need to render a child element into a React application. Easy right? That child is mounted to the nearest DOM element and rendered inside of it as a result.

render() { return ( <div> // Child to render inside of the div </div> ); }

But! What if we want to render that child outside of the div somewhere else? That could be tricky because it breaks the convention that a component needs to render as a new element and follow a parent-child hierarchy. The parent wants to go where its child goes.

That’s where React Portals come in. They provide a way to render elements outside the DOM hierarchy so that elements are a little more portable. It may not be a perfect analogy, but Portals are sort of like the pipes in Mario Bros. that transport you from the normal flow of the game and into a different region.

The cool thing about Portals? Even though they trigger their own events that are independent of the child’s parent element, the parent is still listening to those events, which can be useful for passing events across an app.

We’re going to create a Portal together in this post then make it into a re-usable component. Let’s go!

The example we’re building

Here’s a relatively simple example of a Portal in action:

See the Pen React Portal by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Toggling an element’s visibility is nothing new. But, if you look at the code carefully, you’ll notice that the outputted element is controlled by the button even though it is not a direct descendent of it. In fact, if you compare the source code to the rendered output in DevTools, you’ll see the relationship:

So the outputted element’s parent actually listens for the button click event and allows the child to be inserted even though it and the button are separate siblings in the DOM. Let’s break down the steps for creating this toggled Portal element to see how it all works.

Step 1: Create the Portal element

The first line of a React application will tell you that an App element is rendered on the document root using ReactDOM. Like this;

ReactDOM.render(<App />, document.getElementById("root"));

We need to place the App element in an HTML file to execute it:

<div id="App"></div>

Same sort of thing with Portals. First thing to creating a Portal is to create a new div element in the HTML file.

<div id="portal"></div>

This div will serve as our target. We’re using #portal as the ID, but it doesn’t have to be that. Any component that gets rendered inside this target div will maintain React’s context. We need to store the div as the value of a variable so we can make use of the Portal component that we’ll create:

const portalRoot = document.getElementById("portal");

Looks a lot like the method to execute the App element, right?

Step 2: Create a Portal component

Next, let’s set up the Portal as a component:

class Portal extends React.Component { constructor() { super(); // 1: Create a new div that wraps the component this.el = document.createElement("div"); } // 2: Append the element to the DOM when it mounts componentDidMount = () => { portalRoot.appendChild(this.el); }; // 3: Remove the element when it unmounts componentWillUnmount = () => { portalRoot.removeChild(this.el); }; render() { // 4: Render the element's children in a Portal const { children } = this.props; return ReactDOM.createPortal(children, this.el); } }

Let’s step back and take a look at what is happening here.

We create a new div element in the constructor and set it as a value to this.el. When the Portal component mounts, this.el is appended as a child to that div in the HTML file where we added it. That’s the <div id="portal"></div> line in our case.

The DOM tree will look like this.

<div> // Portal, which is also portalRoot <div> // this.el </div> </div>

If you’re new to React and are confused by the concept of mounting and unmounting an element, Jake Trent has a good explanation. TL;DR: Mounting is the moment the element is inserted into the DOM.

When the component unmounts we want to remove the child to avoid any memory leakage. We will import this Portal component into another component where it gets used, which is the the div that contains the header and button in our example. In doing so, we’ll pass the children elements of the Portal component along with it. This is why we have this.props.children.

Step 3: Using the Portal

To render the Portal component’s children, we make use of ReactDOM.createPortal(). This is a special ReactDOM method that accepts the children and the element we created. To see how the Portal works, let’s make use of it in our App component.

But, before we do that, let’s cover the basics of how we want the App to function. When the App loads, we want to display a text and a button — we can then toggle the button to either show or hide the Portal component.

class App extends React.Component { // The initial toggle state is false so the Portal element is out of view state = { on: false }; toggle = () => { // Create a new "on" state to mount the Portal component via the button this.setState({ on: !this.state.on }); }; // Now, let's render the components render() { const { on } = this.state; return ( // The div where that uses the Portal component child <div> <header> <h1>Welcome to React</h1> </header> <React.Fragment> // The button that toggles the Portal component state // The Portal parent is listening for the event <button onClick={this.toggle}>Toggle Portal</button> // Mount or unmount the Portal on button click <Portal> { on ? <h1>This is a portal!</h1> : null } </Portal> </React.Fragment> </div> ); } }

Since we want to toggle the Portal on and off, we need to make use of component state to manage the toggling. That’s basically a method to set a state of on to either true or false on the click event. The portal gets rendered when on is true; else we render nothing.

This is how the DOM looks like when the on state is set to true.

When on is false, the Portal component is not being rendered in the root, so the DOM looks like this.

More use cases

Modals are a perfect candidate for Portals. In fact, the React docs use it as the primary example for how Portals work:

See the Pen Example: Portals by Dan Abramov (@gaearon) on CodePen.

It’s the same concept, where a Portal component is created and a state is used to append the its child elements to the Modal component.

We can even insert data from an outside source into a modal. In this example, the App component lists users fetched from an API using axios.

See the Pen React Portal 3 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

How about tooltips? David Gilberston has a nice demo:

See the Pen React Portal Tooptip by David Gilbertson (@davidgilbertson) on CodePen.

J Scott Smith shows how Portals can be used to escape positioning:

He has another slick example that demonstrates inserting elements and managing state:

Summary

That’s a wrap! Hopefully this gives you a solid base understanding of Portals as far as what they are, what they do, and how to use them in a React application. The concept may seem trivial, but having the ability to move elements outside of the DOM hierarchy is a handy way to make components a little more extensible and re-usable… all of which points to the core benefits of using React in the first place.

More information

The post Using React Portals to Render Children Outside the DOM Hierarchy appeared first on CSS-Tricks.

Design v17

Css Tricks - Mon, 01/14/2019 - 4:51am

We rolled out a new site design on January 1! This is the 17th version of CSS-Tricks if you can believe that. The versions tend to evolve a decent amount beyond the initial launch, but we archive screenshots on this design history page. Like I said in our 2018 thank you post:

This is easily the most time, effort, and money that's gone into a redesign since the big v10 design. There are a lot of aesthetic changes, but there was also quite a bit of UX work, business goal orientation, workflow tweaking, and backend development work that went along with it.

This is a big one! The reception so far has been pretty great, but please know that we'll be refining it and squishing a lot of bugs here in the early days.

Here are some notes about who was involved, how it happened, and things to notice.

Kylie led this project

Kylie Timpani was the lead designer and really whole project lead on this.

I first reached out to her in April 2017, we chatted in May, and kicked off the work in June. From my perspective, this was a pretty casual process, as I had no particular deadlines and fairly loose goals. Let's make an attractive site that does better in all ways than we do them now.

Kylie was super organized and had a very thoughtful process around every aspect of this. Just in the first block of time that Kylie allocated for this project she:

  • Took a complete content inventory
  • Dug into analytic data to understand users, traffic, and usage at a high level
  • Created, distributed, and analyzed a reader survey to understand readers better and answer specific questions she had for them
  • Chatted with all the staff members of CSS-Tricks to understand their roles, workflows, and ideas

Kylie's obviously not the kind of the designer that just whips open a design tool and starts noodling around. As great of a visual designer as she is, the work was highly informed. She went on to speak with our advertising agency, clearly identify the site's current strengths and weaknesses, and do light wireframing.

I've been using Figma for visual design stuff, and Kylie was happy to use that as the design tool. That was nice since we both have Team level access and were able to use it collaboratively. For me, it was mostly useful for being able to see and reference everything, and make notes on the designs.

We also used Asana to track what was being worked on and ultimately as a place to track bugs and places where the design implementation needed attention.

Thanks so much, Kylie for all your excellent work on this project! If anything is a bit off or buggy about the site, it's my poor implementation. And good luck! ⤵️

⚡️ So! Speaking of big life changing news... in just a couple of weeks I'll be moving to San Francisco! I'm so SO excited to be joining @nrrrdcore and her truly incredible team over at Apple! &#x1f469;&#x1f3fb;‍&#x1f4bb;✨

— Kylie Timpani (@kylietimpani) January 7, 2019

Design Stuff

I'll let y'all explore the design for yourself to find all the little touches we put in, but I'll give a shout out to a few of them where there is a technical detail you might enjoy.

Orange-to-pink

Clearly, we went all dark mode on this design. It's nothing to do with the new media query, although that reminds me we might consider alternations for those who specifically set prefers-color-scheme: light;.

The brand/accent/action colors are orange and pink, which looks quite striking against the darkness but works on light backgrounds as well.

I made a quickie little Sass @mixin that allowed me to use those colors (with variations, if needed) at different angles as backgrounds:

@mixin orange-to-pink($deg: to right, $col-1: $orange, $col-2: $pink) { background-image: linear-gradient($deg, $col-1, $col-2); }

And on text as well wherever we needed (most often as for :hover and :focus):

@mixin gradient-text() { background: linear-gradient(to right, $orange, $pink); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }

We also needed the gradient to be applied to fills and strokes in SVG, so I plopped that into the document to use wherever it was needed.

<svg width="0" height="0" class="visually-hidden"> <defs> <linearGradient id="orange-to-pink" x1="0" x2="0" y1="1" y2="0"> <stop offset="0%" stop-color="#DA1B60" /> <stop offset="100%" stop-color="#ff8a00" /> </linearGradient> </defs> </svg> Fixed header

There is something about headers that always bring out more complexity than you might expect. I recently went through this with the new CodePen header/sidebar and it as complicated for this site. Part of what complicated this one was:

  • It has its own set of unique breakpoints. The header is pretty full, so the breakpoints are pretty specific and unique to it.
  • We wanted a fixed-position (but minified) header that showed up as you scroll down.
  • When you're logged in, there is a WordPress admin bar also fixed to the top of the page. I wanted to accommodate for that.

At one point, it was getting pretty messy and I wound up deleting all the CSS for the entire thing and re-wrote it, taking all the states into consideration, and writing media queries that used logic to clearly specify styles in each of those states.

The idea of a not-always-fixed-position header is interesting in and of itself. It means that:

  • You need to determine when to apply the fixed position
  • You need to make sure the shift from not-fixed to fixed (and back) doesn't cause layout shifting

I was dead nervous about attaching an onscroll listener and doing math and such to determine when to do the switch. I'm sure it can be done responsibly, but I haven't had great luck with that. Instead, I placed a tiny one-pixel element to the screen and attached an IntersectionObserver to it and reacted to that. That gave me the power to adjust where that is in CSS, which was a nice little touch.

Here's the very basics of that code:

See the Pen Fixed Header with IntersectionObserver by Chris Coyier (@chriscoyier) on CodePen.

Mixup

One very cool feature of this design is the Mixup area on the homepage. It was one of Kylie's ideas to show and remind people of the variety and depth of content that is here on CSS-Tricks.

The line that goes through it needs to depend on the height of the HTML content in each of those boxes. The boxes are set on a CSS grid, but they can and should still expand as needed for titles and such. Rather than try to SVG this somehow, the line is essentially stitched together though border and border-radius on individual boxes. To make it line up, I occasionally had to nudge them around with transform.

There was some z-index involved too. It was fun making mistakes along the way:

Cards

I'm kinda in love with native scroll snapping. The cards kinda have a fun animation on desktop, revealing the entire card on hover/focus, and then on mobile you can see the whole card, but are easy to thumb through:

Thanks, Amelia!

The design called for these curved line separators:

I have a small degree of confidence with the SVG path syntax, so I took the first crack at it. I was able to design it in a way that it could draw that line OK and keep the stroke at the desired width, but it didn't scale quite right.

See the Pen Lighted Path by Chris Coyier (@chriscoyier) on CodePen.

I brought in SVG expert Amelia Bellamy-Royds to help me get it right. Feel free to inspect the site to see how it was done. It involves masking and nested SVGs and rectangles and transforms and all sorts of fun stuff. Amelia actually created four variations of the code and carefully noted all the pros and cons of each one. Ultimately, we went with this:

See the Pen Lighted Path by Amelia Bellamy-Royds (@AmeliaBR) on CodePen.

Another thing Amelia helped with was the "circle of text" design element. Kylie had these instances mocked out and I thought they were so cool and I definitely wanted to pull it off. There is a really elaborate way to do it by splitting the characters in to spans and transforming them, but that's a bit messy compared to SVG's <textPath>. I knew I wanted to go the SVG route, but perhaps abstract it away into a reusable component so that it wasn't a heaping pile of code every time I want to use one.

It occurred to me that a web component might be the best way to go here because I can kind of invent the API myself. What I wanted a circle-of-text component to do:

  1. Pass in the text to set on the circle
  2. Declare the radius of the circle
  3. Rotate the circle so I can start the text at any point along the circle

That makes perfect sense as a web component:

<circle-text r="3em" rotate="-90deg"> CSS is super fun & cool & I like CSS!!! </circle-text>

My expertise with web components is limited, so I reached out to Amelia again who is great both with web components and SVG—a perfect match! This is what she was able to do, which I easily integrated easily into this design.

Thanks, Ana!

Another design thing that Kylie cooked up that I was a bit perplexed by was this line:

I thought maybe SVG again, but I really wanted to nestle regular HTML content in there nicely. I was hoping to pull it over with borders or something CSS-y. I reached out to Ana Tudor who is fantastic at tricky design situations and solving them with native browser tech. Ana was able to whip up a good solution here using multiple gradient backgrounds in the main area and a border for the top right bit that flies off.

See the Pen nav by Ana Tudor (@thebabydino) on CodePen.

Thanks, Zach!

Fonts are a unique part of the loading experience of websites in that their presence (or lack of), how they appear, and how they change all play major roles in the perceived performance of the page.

I've had the good fortune of being able to chat with Zach Leatherman about font loading before, but I still don't feel entirely comfortable with what the best practices are in any given situation. For this design of CSS-Tricks, I made the call to use the system font stack for most of the body copy. That has the major benefit of being instantly available to render and aesthetically seems to work well on a technical site, not to mention generally pairing well with Rubik, our header font.

But we still needed to deal with Rubik. There will be an upcoming article from Zach going into this in more details, but the gist is:

  1. Create a minimal subsetted version of Rubik that handles the majority of usage
  2. <link rel="preload" ... > it
  3. Use it with @font-face using font-display
  4. Load a more robust version in an async second stage

Nice job everyone that worked on the @css relaunch!

Look at those web fonts showing up on that 2.09s Fast 3G first render &#x1f389;

(full disclosure I helped a wee bit with the font loading here &#x1f607;) pic.twitter.com/Ih7zJhelQQ

— Zach Leatherman (@zachleat) January 1, 2019

Some areas of the site are somewhat deprecated

The Forums is such a complicated area of the site to design and maintain, what I've done is just loaded the default bbPress styling for them, instead of trying to override things or start from scratch. I think that'll be the best route going forward.

There is a Gallery section of this site, but I'm not even linking to it anymore as we didn't really keep it up to date very well nor did it get used much. The URL's still work though. Maybe it can make a return someday, but for now, I'm enjoying the reduction of some technical and content debt.

Tech stack

It's somewhat boring. It's about the same thing I've done forever. It's a stock install of WordPress with a custom theme, a dozen or so plugins, and a bit of custom-coded functionality, like having the images powered by Cloudinary. It's running on a custom Media Temple-built box so it can have PHP 7 and MySQL 5.6, plus a firewall that also acts as a CDN. It's nice to have a pretty snappy foundation, so it's on me as a front-end dev to keep it that way.

I used SVG for the icons, Sass for the styling, and Babel to write jQuery-based functionality in ES6. I wrote up a Gulp file to do all that processing and run the local BrowserSync dev server. Local WordPress via Local by Flywheel.

I'm actually pretty happy with the stack as it felt quick and productive to me. But I admit, part of me wishes I dug a little harder into new tech, like building webpack-based processing or trying to go all-in on a server-rendered and React-powered headless WordPress via GraphQL kinda thing. The reason I didn't is because boring has served me so well and time is a major factor since I'm developing alone (my budget doesn't exactly make available a whole development team). My guess is a major front-end infastructure overhaul would have tripled the dev time for questionable benefits. It still sounds like fun and might open up future doors, but hey, another time.

My last regret is that I wish I had spun up a real pattern library system from the start. I think I did OK in breaking things up into reusable parts, but the site isn't truly componentized. As I approached the finish line, I started to see how this could have gone a bit smoother for me should I have worked with true components that accepted data and had variations and such. Native PHP isn't great for that, so it would have forced me into some kind of templating system, and I probably wouldn't have regretted it. If I stay in PHP next time, maybe I'd use something like Timber and Twig for all the components, and then Fractal for the pattern library since it supports Twig. I kind of dig the way Timber abstracts the data stuff from the views.

I hadn't heard of this app until now, but check out Project Wallace:

Project Wallace is a project aimed at gaining insights in your CSS over a longer period of time. It started a couple of years ago as a frustration with existing CSS analyzers that only do a one-time only analysis. As time went by, more and more features were added and now Wallace is place to go for developers who want to know if their complexicity has increased or for a designer who wants to know if all the correct colors and fonts are being used.

Bart Veneman set it up to watch CSS-Tricks, and you can see a before/after comparison and charts over time. Bart blogged about the numbers for us as well. Thanks Bart!

There are more interesting stats in that blog post. CodePen embeds

The true usefulness of CodePen Embed Themes came out here. The whole point of an embed theme is that you can use them to match the design of where the Pens will be embedded, and if you need to change that design, you can change them all in one fell swoop. There are probably thousands of embedded Pens on this site, and they all got updated at once with one theme change.

There are a few special things that I've done with CodePen embeds on this site:

  • The are resizable from the bottom right corner. Used jQuery. Like this.
  • They have a placeholder height. When you embed a Pen, you can choose how tall you want it to be. That's how tall the <iframe> will come in as. But I've adjust it so that the <p> that is there before the iframe comes in will be that same height, so there is no reflow jank.

I regex'd that sucker myself like this:

function codepen_reflow_fix($content) { $content = preg_replace('/data-height=(\'|")(\d*)(\'|")/', 'data-height="$2" style="height: $2px;"', $content); $content = preg_replace('/data-theme-id="\d*"/', 'data-theme-id="1"', $content); return $content; } add_filter('the_content', 'codepen_reflow_fix', 10);

We're gonna bring that feature to CodePen itself real soon. Notice in that RegEx above I'm also forcing the theme id. That way, all embedded Pens definitely have the correct theme, even if we forget.

Achievement unlocked: The custom scrollbar is the new feature that everyone either loves or hates

If there has been one constant in every CSS-Tricks design, it's that there's at least one feature people either love or hate. This time, I'm happy to announce it's the custom scrollbar. In a sense, it's for myself. I manually use scrollbars quite a bit and it feels both fun and highly usable to grab onto this big beefy chunk of pink love.

It's also a little inspired by VS Code, which features a pretty beefy scrollbar itself:

There are general usability considerations about custom scrollbars for sure, but I don't feel like they've been breached too heavily here, if at all. I've heard some "don't mess with my browsers UI" feedback, which I sorta get, but does that mean we shouldn't style any form controls, or even use CSS at all? (LOL.) And don't scrollbars come from the system, not the browser?

Anyway, I'm not faking them or anything. I'm just using ::-webkit-scrollbar and friends. There is official scrollbar styling stuff on the way, per the CSS specs. I didn't use any of that stuff/ I think I'll wait for at least one browser to support it.

We have plenty of bug fixing and polishing to do still on this design. If you've emailed or tweeted or communicated with us in some way about it, I've probably seen it and have been log it all to make sure it's all addressed the best we can. Plus stay tuned for some fun new features!

If you have thoughts, free to leave comments here if you like, use our contact form, email at team@css-tricks.com, or chat it up in our new Spectrum community.

The post Design v17 appeared first on CSS-Tricks.

The Ethics of Web Performance

Css Tricks - Mon, 01/14/2019 - 4:46am

Tim Kadlec on the issues surrounding poor web performance and why it’s so important for us to care about making our sites as fast as possible:

Poor performance can, and does, lead to exclusion. This point is extremely well documented by now, but warrants repeating. Sites that use an excess of resources, whether on the network or on the device, don’t just cause slow experiences, but can leave entire groups of people out.

There is a growing gap between what a high-end device can handle and what a middle to low-end device can handle. When we build sites and applications that include a lot of CPU-bound tasks (hi there JavaScript), at best, those sites and applications become painfully slow on people using those more affordable, more constrained devices. At worst, we ensure that our site will not work for them at all.

Forget about comparing this year’s device to a device a couple of years old. Exclusion can happen on devices that are brand-new as well. The web’s growth is being pushed forward primarily by low-cost, underpowered Android devices that frequently struggle with today’s web.

As Tim mentions at the end of that piece though, it’s easy to forget web performance and it’s sometimes hard to make the case for making a website fast. It’s often seen as a nice-to-have instead of as a core feature in and of itself, like semantic markup and accessibility compliance.

I’m optimistic that the conversation surrounding this topic is improving things though. Having tools like Lighthouse built straight into the browser makes things easier and the abundance of testing tools such as Calibre gives us insights into exactly what and where issues might be. But we also need to remember that this isn’t solely a technical problem — it’s an ethical one, too.

Direct Link to ArticlePermalink

The post The Ethics of Web Performance appeared first on CSS-Tricks.

Slice and Dice a Disc with CSS

Css Tricks - Sun, 01/13/2019 - 12:05pm

I recently came across an interesting sliced disc design. The disc had a diagonal gradient and was split into horizontal slices, offset a bit from left to right. Naturally, I started to think what would the most efficient way of doing it with CSS be.

Sliced gradient disc.

The first thought was that this should be doable with border-radius, right? Well, no! The thing with border-radius is that it creates an elliptical corner whose ends are tangent to the edges it joins.

My second thought was to use a circle() clipping path. Well, turns out this solution works like a charm, so let's take a close look at it!

Note that the following demos won't work in Edge as Edge doesn't yet support clip-path on HTML elements. It could all be emulated with nested elements with overflow: hidden in order to have cross-browser support, but, for simplicity, we dissect the clip-path method in this article.

Slicing a disc into equal parts

As far as the HTML structure goes, we generate it with a preprocessor to avoid repetition. First off, we decide upon a number of slices n. Then we pass this number to the CSS as a custom property --n. Finally, we generate the slices in a loop, passing the index of each to the CSS as another custom property --i.

- var n = 8; style :root { --n: #{n} } - for(var i = 0; i < n; i++) .slice(style=`--i: ${i}`)

Moving on to the CSS, we first decide upon a diameter $d for our disc. This is the width of our slices. The height is the diameter divided by the number of items calc(#{$d}/var(--n)).

In order to be able to tell them apart, we give our slices dummy backgrounds determined by parity.

$d: 20em; .slice { --parity: 0; width: $d; height: calc(#{$d}/var(--n)); background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%)); &:nth-of-type(2n) { --parity: 1 } }

We also position our slices in the middle with a column flex layout on their container (the body in our case).

See the Pen by thebabydino (@thebabydino) on CodePen.

To get the disc shape we use a circle() clipping path having the radius $r equal to half the diameter .5*$d and the central point dead in the middle of the assembly. Since we set this clip-path on the slices, the position of the central point for each slice is relative to the slice itself.

Horizontally, it's always in the middle, at 50% of the slice. Vertically, it needs to be in the middle of the assembly, so that's where the total number of items and the item's index which we've passed as CSS variables from the preprocessor code come into play.

In the middle of the assembly means at half the height of the assembly from the top of the assembly. Half the height of the assembly is half the diameter .5*$d, which is equivalent to the radius $r. But this value is relative to the whole assembly and we need one that's relative to the current slice. In order to get this, we subtract the vertical position of the current slice relative to the assembly, that is, how far the top of the current slice is relative to the top of the assembly.

The first slice (of index --i: 0) is at the very top of the assembly, so the amount we subtract in this case is 0.

The second slice (of index --i: 1) is at one slice height from the top of the assembly (the space occupied by the first slice), so the amount we subtract in this case is 1 slice heights.

The third slice (of index --i: 2) is at two slice heights from the top of the assembly (the space occupied by the first and second slices), so the amount we subtract in this case is 2 slice heights.

In the general case, the amount we subtract for each slice is the slice's index (--i) multiplied by one slice height.

--h: calc(#{d}/var(--n)); /* slice height */ clip-path: circle($r at 50% calc(#{$r} - var(--i)*var(--h))

See the Pen by thebabydino (@thebabydino) on CodePen.

After doing this, we can offset the slices based on parity.

--sign: calc(1 - 2*var(--parity)); transform: translate(calc(var(--sign)*2%))

We now have our sliced disc!

See the Pen by thebabydino (@thebabydino) on CodePen.

Spacing out the slices

The first thought that comes to mind here is to use a margin on each slice.

See the Pen by thebabydino (@thebabydino) on CodePen.

This may be a good result in some cases, but what if we don't want our disc to get elongated?

Well, we have the option of limiting the background to the content-box and adding a vertical padding:

box-sizing: border-box; padding: .125em 0; background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%)) content-box;

Of course, in this case, we need to make sure box-sizing is set to border-box so that the vertical padding doesn't add to the height.

See the Pen by thebabydino (@thebabydino) on CodePen.

The one little problem in this case is that it also cuts off the top of the first slice and the bottom of the last slice. This may not be an issue in some cases and we can always reset the padding-top on the :first-of-type and the padding-bottom on the :last-of-type to 0:

.slice { /* other styles */ padding: .125em 0; &:first-of-type { padding-top: 0 } &:last-of-type { padding-bottom: 0 } }

However, we also have a one-line solution to this problem of creating gaps in between the slices: add a mask on the container!

This mask is a repeating-linear-gradient() which creates transparent stripes of the thickness of the gap $g, repeats itself after a slice height and is limited to the disc diameter $d horizontally and to the disc diameter $d minus a gap $g vertically (so that we don't mask out the very top and the very bottom as we also did initially with the padding approach).

mask: repeating-linear-gradient(red 0, red calc(var(--h) - #{$g}), transparent 0, transparent var(--h)) 50% calc(50% - #{.5*$g})/ #{$d} calc(#{$d} - #{$g})

Note that in this case we need to set the slice height variable --h on the container as we're using it for the mask.

See the Pen by thebabydino (@thebabydino) on CodePen.

Continuous background

In order to have a continuous gradient background, we need to give this background a height equal to that of the disc and set its vertical position relative to each slice such that it always starts from the top of the assembly... wherever that may be located relative to the slice.

The top of the first slice (of index --i: 0) coincides with that of the assembly, so our background starts from 0 vertically.

The top of the second slice (of index --i: 1) is 1 slice height below that of the assembly, so its background starts from 1 slice height above vertically. Since the positive direction of the y axis is down, this means our background-position along the y axis is calc(-1*var(--h)) in this case.

The top of the third slice (of index --i: 2) is 2 slice heights below that of the assembly, so its background starts from 2 slice heights above vertically. This makes our background-position along the y axis is calc(-2*var(--h)).

We notice a pattern here: in general, the background-position along the y axis for a slice is calc(-1*var(--i)*var(--h)).

background: linear-gradient(#eccc05, #c26e4c, #a63959, #4e2255, #333f3d) /* background-position */ 50% calc(-1*var(--i)*var(--h))/ 100% $d /* background-size */

See the Pen by thebabydino (@thebabydino) on CodePen.

But if we want a left to right gradient, then our background isn't continuous anymore, something that becomes really obvious if we tweak the stop positions a bit in order to have abrupt changes:

background: linear-gradient(90deg, #eccc05 33%, #c26e4c 0, #a63959 67%, #4e2255 0, #333f3d) /* background-position */ 50% calc(-1*var(--i)*var(--h))/ 100% $d /* background-size */

See the Pen by thebabydino (@thebabydino) on CodePen.

In order to fix this issue, we set the offset as a Sass variable $o, set the horizontal background-size to the slice width (100% or $d) plus twice the offset and make sure we attach the background for the slices that move to the left (in the negative direction of the x axis, so by -$o) on the left side of the slice (background-position along the x axis is 0%) and for the slices that move to the right (in the positive direction of the x axis, so by $o) on the right side of the slice (background-position along the x axis is 100%).

$o: 2%; transform: translate(calc(var(--sign)*#{$o})); background: linear-gradient(90deg, #eccc05 33%, #c26e4c 0, #a63959 67%, #4e2255 0, #333f3d) /* background-position */ calc((1 - var(--parity))*100%) calc(-1*var(--i)*var(--h))/ calc(100% + #{2*$o}) $d /* background-size */

See the Pen by thebabydino (@thebabydino) on CodePen.

This works for gradients at any angle, as it can be seen in the interactive demo below - drag to change the gradient angle:

See the Pen by thebabydino (@thebabydino) on CodePen.

It also works for images, though in this case we need to remove the second background-size value so the image doesn't get distorted, which leaves us with the caveat of getting vertical repetition if the image's aspect ratio is greater than calc(#{$d} + #{2*$o}) : #{$d}. This isn't the case for the square image we're using below, but it's still something to keep in mind.

See the Pen by thebabydino (@thebabydino) on CodePen.

Another thing to note is that above, the top of the image is attached to the top of of the assembly. If we want the middle of the image to be attached to the middle of the assembly, we need to tweak the vertical component of the background-position a bit.

First off, to attach the middle of the image to the middle of a slice, we use a background-position value of 50%. But we don't want the middle of the image in the middle of each slice, we want it in the middle of the assembly for all slices. We already know the distance from the top of each slice to the vertical midpoint of the whole assembly - it's the y coordinate of the clipping circle's central point:

--y: calc(#{$r} - var(--i)*var(--h)); clip-path: circle($r at 50% var(--y))

The distance from the vertical midpoint of each slice to that of the assembly is this value --y minus half a slice's height. So it results that the background-position we need along the y axis in order to have the vertical midpoint of the image attached to that of the assembly is calc(50% + var(--y) - .5*var(--h)).

See the Pen by thebabydino (@thebabydino) on CodePen.

Incremental slices

This means our slices don't have the same height anymore. For example, the first one could have a unit height, the second one twice this height, the third one three times this height and so on...

The added heights of all these slices should equal the disc diameter. In other words, we should have the following equality:

h + 2*h + 3*h + ... + n*h = d

This can also be written as:

h*(1 + 2 + 3 + ... + n) = d

which makes it easier to notice something! Within the parenthesis, we have the sum of the first n natural numbers, which is always n*(n + 1)/2!

So our equality becomes:

h*n*(n + 1)/2 = d

This allows us to get the unit height h:

h = 2*d/n/(n + 1)

Applying this to our demo, we have:

--h: calc(#{2*$d}/var(--n)/(var(--n) + 1)); height: calc((var(--i) + 1)*var(--h));

See the Pen by thebabydino (@thebabydino) on CodePen.

Just like in the case of equal slices, the y coordinate of the central point of the clipping circle() is the disc radius $r minus the distance from the top of the assembly to the top of the current slice. This is the sum of the heights of all previous slices.

In the case of the first slice (--i: 0), we have no previous slice, so this sum is 0.

In the case of the second slice (--i: 1), we only have the first slice before and its height is the unit height (--h).

In the case of the third slice (--i: 2), the sum we want is that between the height of the first slice, which equals the unit height and that of the second slice, which is twice the unit height. That's calc(var(--h) + 2*var(--h)) or calc(var(--h)*(1 + 2)).

In the case of the third slice (--i: 3), the sum is that between the height of the first slice, which equals the unit height, that of the second slice, which is twice the unit height and that of the third slice, which is three times the unit height. That's calc(var(--h) + 2*var(--h) + 3*var(--h)) or calc(var(--h)*(1 + 2 + 3)).

Now we can see a pattern emerging! For every slice of index --i, we have that the added height of its previous slices is the unit height --h times the sum of the first --i natural numbers (and the sum of the first --i natural numbers is calc(var(--i)*(var(--i) + 1)/2)). This means our clip-path value becomes:

circle($r at 50% calc(var(--h)*var(--i)*(var(--i) + 1)/2))

We add the offset back in and we have the following result:

See the Pen by thebabydino (@thebabydino) on CodePen.

Sadly, having incremental slices means the repeating-linear-gradient() mask method of creating gaps cannot work anymore. What still works however just fine is the vertical padding method and we can set the padding values such that the top one is 0 for the first slice and the bottom one is 0 for the last slice.

padding: calc(var(--i)*#{$g}/var(--n)) /* top */ 0 /* lateral */ calc((var(--n) - 1 - var(--i))*#{$g}/var(--n)) /* bottom */

See the Pen by thebabydino (@thebabydino) on CodePen.

For a gradient background, the main idea remains the same as in the case of the equal slices. There are just two things we need to take into account.

One, the background-position along the y axis is minus the distance (in absolute value) between the top of the assembly and the top of the current slice. This distance isn't calc(var(--i)*var(--h)) like in the case of equal slices of height --h anymore. Instead it's, as computed a bit earlier, calc(var(--i)*(var(--i) + 1)/2*var(--h)). So the background-position along the y axis is calc(-1*var(--i)*(var(--i) + 1)/2*var(--h)).

And two, we want our background clipped to the content-box so that we keep the gaps, but we need to keep the background-origin to its initial value of padding-box so that our gradient stays continuous.

background: linear-gradient(var(--a), #eccc05, #c26e4c, #a63959, #4e2255, #333f3d) /* background-position */ calc((1 - var(--parity))*100%) /* x component */ calc(-1*var(--i)*(var(--i) + 1)/2*var(--h)) /* y component */ / /* background-size */ calc(100% + #{2*$o}) $d padding-box /* background-origin */ content-box /* background-clip */;

See the Pen by thebabydino (@thebabydino) on CodePen.

For an image background whose midpoint is attached to the middle of our assembly, we need to take into account the fact that half a slice height isn't the same value for all slices anymore. Now the height of a slice is calc((var(--i) + 1)*var(--h)), so this is the value we need to subtract in the formula for the y component of the background-position.

--y: calc(#{$r} - .5*var(--i)*(var(--i) + 1)*var(--h)); background: url(/amur_leopard.jpg) /* background-position */ calc((1 - var(--parity))*100%) /* x component */ calc(50% + var(--y) - .5*(var(--i) + 1)*var(--h)) /* y component */ / /* background-size */ calc(100% + #{2*$o}) padding-box /* background-origin */ content-box /* background-clip */; clip-path: circle($r at 50% var(--y));

See the Pen by thebabydino (@thebabydino) on CodePen.

Vertical slices

We can also slice our disc along the other direction. This means removing the flex-direction: column declaration from the container and letting the flex-direction be the initial one (row), switching the width and the height, the x and y coordinates of the circular clipping path's central point, the direction along which we shift the slices, the dimensions and x and y positions of the masking gradient, which we also need to rotate so that it goes along the x axis.

body { /* same as before */ --w: calc(#{$d}/var(--n)); mask: repeating-linear-gradient(90deg, red 0, red calc(var(--w) - #{$g}), transparent 0, transparent var(--w)) calc(50% - #{.5*$g}) 50% / calc(#{$d} - #{$g}) #{$d} } .slice { /* same as before */ width: var(--w); height: $d; transform: translatey(calc(var(--sign)*2%)); background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%)); clip-path: circle($r at calc(#{$r} - var(--i)*var(--w)) 50%) }

This gives us equal vertical slices with alternating backgrounds:

See the Pen by thebabydino (@thebabydino) on CodePen.

For the gradient case, we need to also reverse the two background dimensions and the background positions along the x and y axes:

background: linear-gradient(135deg, #eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%) /* background-position */ calc(-1*var(--i)*var(--w)) calc((1 - var(--parity))*100%)/ #{$d} calc(100% + #{2*$o}) /* background-size */

See the Pen by thebabydino (@thebabydino) on CodePen.

For incremental slices, we combine the incremental case with the vertical case, which means swapping the values we have for the previous incremental case along the two axes:

--w: calc(#{2*$d}/var(--n)/(var(--n) + 1)); width: calc((var(--i) + 1)*var(--w)); height: $d; clip-path: circle($r at calc(#{$r} - .5*var(--i)*(var(--i) + 1)*var(--w)) 50%);

See the Pen by thebabydino (@thebabydino) on CodePen.

To create the gaps, we use the padding method. But since we're now in the vertical case, we need horizontal paddings, on the left and on the right and to make sure the padding-left for the first slice is 0 and the padding-right for the last slice is also 0:

box-sizing: border-box; padding: 0 /* top */ calc((var(--n) - 1 - var(--i))*#{$g}/var(--n)) /* right */ 0 /* bottom */ calc(var(--i)*#{$g}/var(--n)) /* left */; background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%)) content-box

See the Pen by thebabydino (@thebabydino) on CodePen.

Finally, we have the gradient case:

background: linear-gradient(135deg, #eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%) /* background-position */ calc(-.5*var(--i)*(var(--i) + 1)*var(--w)) /* x component */ calc((1 - var(--parity))*100%) /* y component */ / /* background-size */ #{$d} calc(100% + #{2*$o}) padding-box /* background-origin */ content-box /* background-clip */;

See the Pen by thebabydino (@thebabydino) on CodePen.

2D case

Again, we generate it with a bit of Pug, the total number of items being the product between the number of columns and the number of rows. For simplicity, we keep the number of rows and the number of columns equal.

- var n = 8, m = Math.pow(n, 2); style :root { --n: #{n}; --i: 0; --j: 0 } - for(var i = 1; i < n; i++) { | .tile:nth-of-type(#{n}n + #{i + 1}) { --i: #{i} } | .tile:nth-of-type(n + #{n*i + 1}) { --j: #{i} } - } - for(var i = 0; i < m; i++) .tile

We've also passed the column and row indices (--i and --j respectively) to the CSS.

Since we're in the 2D case, we switch from using a 1D layout (flex) to using a 2D one (grid). We also start with the disc diameter $d and, given the number of columns is equal to that of rows (--n), our disc gets divided into identical tiles of edge length --l: calc(#{$d}/var(--n)).

$d: 20em; body { --l: calc(#{$d}/var(--n)); display: grid; place-content: center; grid-template: repeat(var(--n), var(--l))/ repeat(var(--n), var(--l)) }

To create the gaps in between the tiles, we use the padding approach on the .tile elements and combine the horizontal and vertical cases such that we have the padding-top for the first row is 0, the padding-left for the first column is 0, the padding-bottom for the last row is 0 and the padding-right for the last-column is 0.

padding: calc(var(--j)*#{$g}/var(--n)) /* top */ calc((var(--n) - 1 - var(--i))*#{$g}/var(--n)) /* right */ calc((var(--n) - 1 - var(--j))*#{$g}/var(--n)) /* bottom */ calc(var(--i)*#{$g}/var(--n)) /* left */

Note that we've used the row index --j for the top to bottom direction (vertical paddings) and the column index --i from the left to right direction (lateral paddings).

To get the disc shape, we again combine the horizontal and vertical cases, using the column index --i to get the x coordinate of the circular clipping path's central point and the row index --j to get its y coordinate.

clip-path: circle($r at calc(#{$r} - var(--i)*var(--l)) calc(#{$r} - var(--j)*var(--l)))

See the Pen by thebabydino (@thebabydino) on CodePen.

For a gradient background, it's again combining the horizontal and the vertical cases and taking into account that here we have no offset at this point, which means the background-size is the disc diameter $d along both axes.

background: linear-gradient(135deg, #eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%) /* background-position */ calc(-1*var(--i)*var(--l)) calc(-1*var(--j)*var(--l)) / #{$d} #{$d} /* background-size */ padding-box /* background-origin */ content-box /* background-clip */

See the Pen by thebabydino (@thebabydino) on CodePen.

For an image background, we remove the second background-size value so we prevent the image from getting stretched if it's not square. We also adapt the code for attaching the image's midpoint to that of the grid from the 1D case to the 2D case:

--x: calc(#{$r} - var(--i)*var(--l)); --y: calc(#{$r} - var(--j)*var(--l)); background: url(/amur_leopard.jpg) /* background-position */ calc(50% + var(--x) - .5*var(--l)) calc(50% + var(--y) - .5*var(--l)) / #{$d} /* background-size */ padding-box /* background-origin */ content-box /* background-clip */; clip-path: circle($r at var(--x) var(--y))

See the Pen by thebabydino (@thebabydino) on CodePen.

In the incremental case, we don't have the same dimensions for all tiles, so we use auto sizing for grid-template:

body { /* same as before */ grid-template: repeat(var(--n), auto)/ repeat(var(--n), auto) }

Just like in the 1D case, we start by computing a unit edge length --u:

--u: calc(#{2*$d}/var(--n)/(var(--n) + 1))

We then set incremental dimensions along both axes for our tile elements:

width: calc((var(--i) + 1)*var(--u)); height: calc((var(--j) + 1)*var(--u))

We also need to adapt the coordinates of the clipping circle's central point to the incremental case:

clip-path: circle($r at calc(#{$r} - .5*var(--i)*(var(--i) + 1)*var(--u)) calc(#{$r} - .5*var(--j)*(var(--j) + 1)*var(--u)))

See the Pen by thebabydino (@thebabydino) on CodePen.

For a gradient background, we adapt the equal tiles version to the incremental case. This means tweaking the background-position as we did before for the incremental slices, only this time we do it along both axes, not just along one:

background: linear-gradient(135deg, #eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%) /* background-position */ calc(-.5*var(--i)*(var(--i) + 1)*var(--l)) calc(-.5*var(--j)*(var(--j) + 1)*var(--l)) / #{$d} #{$d} /* background-size */ padding-box /* background-origin */ content-box /* background-clip */

See the Pen by thebabydino (@thebabydino) on CodePen.

Finally, we have the image background option for the incremental 2D case:

background: url(/amur_leopard.jpg) /* background-position */ calc(50% + var(--x) - .5*(var(--i) + 1)*var(--u)) calc(50% + var(--y) - .5*(var(--j) + 1)*var(--u)) / #{$d} /* background-size */ padding-box /* background-origin */ content-box /* background-clip */

See the Pen by thebabydino (@thebabydino) on CodePen.

There are probably more variations we could be coming up with, but we'll stop here. If you have more ideas on how to push this further, I'd love to hear about them!

The post Slice and Dice a Disc with CSS appeared first on CSS-Tricks.

Re: Pleasing Color Palettes

Css Tricks - Fri, 01/11/2019 - 11:43am

There are so many tools out there to help you pick colors. I totally get it! It's hard! When colors are done well, it's like magic. It adds a level of polish to a design that can really set it apart.

Let's look at some, then talk about this idea some more.

Here's one I just saw called Color Koala:

It spits out five colors at ya and you're off to the races.

Hue will give you some too.

There's a billion more, and they vary in approach and features, of course. Here’s a handful:

Then there are tools that focus on gradients, like UI Gradients, Web Gradients, and Shapy.

Oh! And a site that helps with text color while keeping accessibility in mind.

There are even native apps like Sip, ColorSnapper, and Frank DeLoupe that help you select colors and sometimes keep your palettes right within them.

Colors can be programatically generated.

There is no native JavaScript API for it, but it's still basically a one-liner:

See the Pen Generate New Random Hex Color with JavaScript by Chris Coyier (@chriscoyier) on CodePen.

Pleasing colors can be as well.

Generating random colors won’t guarantee pleasing palettes, especially if a bunch of random colors are paired together. PleaseJS can help build color schemes that work together. You provide it a base color and other options (like what type of color scheme) and it spits out colors for you.

See the Pen Generate Pleasing Colors by Chris Coyier (@chriscoyier) on CodePen.

Similarly, randomColor.js...

gen­er­ates at­trac­tive col­ors by de­fault. More specif­i­cally, ran­dom­Color pro­duces bright col­ors with a rea­son­ably high sat­u­ra­tion. This makes ran­dom­Color par­tic­u­larly use­ful for data vi­su­al­iza­tions and gen­er­a­tive art.

It doesn't claim to make multiple colors part of a cohesive theme aside from passing in a base hue or luminosity.

See the Pen Generate Pleasing Colors by Chris Coyier (@chriscoyier) on CodePen.

But the thing about just being handed colors is...

...they don't exactly tell you how to use them. Steve Schoger makes a point of this, rather hilariously in a blog post. This is a perfectly lovely color palette:

But if you just pick those colors and plop them onto a design, you could end up with something like this:

You might like that, but you'd be in the minority. It's not a refined design that gets out of the way and would be nice to use every day. Color usage is a bit more complicated than plopping five nice colors into a design. It's variations on those and using them in tasteful ways, like this:

Picking up Steve Schoger and Adam Wathan's book surely has some advice for you there!

The post Re: Pleasing Color Palettes appeared first on CSS-Tricks.

Piecing Together Approaches for a CSS Masonry Layout

Css Tricks - Fri, 01/11/2019 - 5:06am

Masonry layout, on the web, is when items of an uneven size are laid out such that there aren't uneven gaps. I would guess the term was coined (or at least popularized) for the web by David DeSandro because of his popular Masonry JavaScript library, which has been around since 2010.

JavaScript library. Nothing against JavaScript, but it's understandable we might not want to lean on it for doing layout. Is there anything we can do in CSS directly these days? Sorta.

Is vertical order with ragged bottoms OK?

If it is, then CSS columns will do just fine.

See the Pen Masonry with Columns by Chris Coyier (@chriscoyier) on CodePen.

Flexbox can do vertical columns with ragged endings too

But it's not quite as clever, because you'll need to set a height of some kind to get it to wrap the columns. You'll also have to be explicit about widths rather than having it decide columns for you.

But it's doable and it does auto space the gaps if there is room.

See the Pen Masonry with Flexbox by Chris Coyier (@chriscoyier) on CodePen.

Do you need a clean bottom edge? A Flexbox/JavaScript combo can help.

Jamie Perkins originally wrote this, then Janosh Riebesell re-wrote it and, now I'm porting it here.

It totally messes with the order and requires the children to be flexy about their height, but it does the trick:

See the Pen Masonry with Flexbox + JS by Chris Coyier (@chriscoyier) on CodePen.

Is horizontal line masonry OK?

If it's just the uneven brick-like look you're after, then horizontal masonry is way easier. You could probably even float stuff if you don't care about the ragged edge. If you wanna keep it a block... flexbox with allowed flex-grow is the ticket.

See the Pen Masonry with Flexbox + JS by Chris Coyier (@chriscoyier) on CodePen.

You'd think CSS grid could help

CSS grid is very amazing and useful in a CSS developer’s everyday life, but it's not really designed for masonry style layouts. CSS grid is about defining lines and placing things along those lines, where masonry is about letting elements end where they may, but still exerting some positional influence.

Balázs Sziklai has a nice example of auto-flowing grids that all stack together nicely, with pretty good horiziontal ordering:

See the Pen True Masonry with Grid Layout by Balázs Sziklai (@balazs_sziklai) on CodePen.

But you can see how strict the lines are. There is a way though!

Grid + JavaScript-manipulated row spans

Andy Barefoot wrote up a great guide. The trick is setting up repeating grid rows that are fairly short, letting the elements fall into the grid horizontally as they may, then adjusting their heights to match the grid with some fairly light math to calculate how many rows they should span.

See the Pen CSS Grid Masonry (Step 10) by Andy Barefoot (@andybarefoot) on CodePen.

Rahul Arora went down this road as well:

See the Pen Rahul Arora's Left-to-right Masonry Layout using CSS Grid by Chris Coyier (@chriscoyier) on CodePen.

DOM-shifted elements in a CSS columns layout

What people generally want is column-stacking (varied heights on elements), but with horizontal ordering. That last grid demo above handles it pretty well, but it's not the only way.

Jesse Korzan tackled it with CSS columns. It needs JavaScript as well to get it done. In this case, it shifts the elements in the DOM to order them left-to-right while providing a horizontal stack using a CSS columns layout. This introduces a bit of an accessibility problem since the visual order (left-to-right) and source order (top-to-bottom) are super different & dash; though perhaps fixable with programmatic tabindex?

There’s also the original library and variations

Float away, my pretties.

See the Pen Masonry with Masonry by Chris Coyier (@chriscoyier) on CodePen.

And it's newer, hipper verion: Colcade!

See the Pen Masonry with Colcade by Chase (@chasebank) on CodePen.

And here's MagicGrid, in which a flexbox layout is lightly manipulated with a JavaScript lib:

See the Pen Magic Grid by Chris Coyier (@chriscoyier) on CodePen.

The post Piecing Together Approaches for a CSS Masonry Layout appeared first on CSS-Tricks.

Why we need CSS subgrid

Css Tricks - Fri, 01/11/2019 - 5:03am

I’m a huge fan of CSS Grid and I use it on pretty much every project these days. However, there’s one part of it that makes things much more complicated than they really ought to be: the lack of subgrids. And in this post on the matter, Ken Bellows explains why they’d be so gosh darn useful:

But one thing still missing from the Level 1 spec is the ability to create a subgrid, a grid-item with its own grid that aligns in one or both dimensions with the parent grid. It was originally planned to be in Level 1, but the working group decided they needed more time to work out the details, so it was removed, and it will ship in CSS Grid Layout Module Level 2, which seems to be nearing completion.

There has been a lot of discussion over the last 2 years about the use cases for subgrid, how it should be implemented, and even some debate over whether you even need it. A lot of that discussion was centered around two other approaches that can handle many of the same problems as subgrid: nested grids and display: contents

I remember one of the very first websites I worked on was much like the demo that Ken uses as an example, but this was way back in 2012 and grid didn’t exist yet. Sadly, I had to write a lot more CSS than I felt was necessary to get elements in one div to line up with elements in another. Anyway, this article kinda riffs off of Rachel Andrew’s post about subgrid and what problems it would help solve which is definitely worth checking out, too.

Direct Link to ArticlePermalink

The post Why we need CSS subgrid appeared first on CSS-Tricks.

Converting Color Spaces in JavaScript

Css Tricks - Thu, 01/10/2019 - 5:07am

A challenge I faced in building an image "emojifier" was that I needed to change the color spaces of values obtained using getImageData() from RGB to HSL. I used arrays of emojis arranged by brightness and saturation, and they were HSL-based for the best matches of average pixel colors with the emojis.

In this article, we’ll study functions that will be useful for converting both opaque and alpha-enabled color values. Modern browsers currently support the color spaces RGB(A), hex, and HSL(A). The functions and notations for these are rgb(), rgba(), #rgb/#rrggbb, #rgba/#rrggbbaa, hsl(), and hsla(). Browsers have always supported built-in names like aliceblue as well.

Along the way, we’ll encounter use of some color syntaxes provided by a new Level 4 of the CSS Colors Module. For example, we now have hex with alpha as we mentioned (#rgba/#rrggbbaa) and RGB and HSL syntaxes no longer require commas (values like rgb(255 0 0) and hsl(240 100% 50%) became legal!).

Browser support for CSS Colors Level 4 isn’t universal as of this writing, so don’t expect new color syntaxes to work in Microsoft browsers or Safari if trying them in CSS.

RGB to Hex

Converting RGB to hex is merely a change of radices. We convert the red, green, and blue values from decimal to hexadecimal using toString(16). After prepending 0s to single digits and under, we can concatenate them and # to a single return statement.

function RGBToHex(r,g,b) { r = r.toString(16); g = g.toString(16); b = b.toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; return "#" + r + g + b; } RGB in String

Alternatively, we can use a single string argument with the red, green and blue separated by commas or spaces (e.g. "rgb(255,25,2)", "rgb(255 25 2)"). Substring to eliminate rgb(, split what’s left by the ), then split that result’s first item by whichever the separator (sep) is. r, g, and b shall become local variables now. Then we use + before the split strings to convert them back to numbers before obtaining the hex values.

function RGBToHex(rgb) { // Choose correct separator let sep = rgb.indexOf(",") > -1 ? "," : " "; // Turn "rgb(r,g,b)" into [r,g,b] rgb = rgb.substr(4).split(")")[0].split(sep); let r = (+rgb[0]).toString(16), g = (+rgb[1]).toString(16), b = (+rgb[2]).toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; return "#" + r + g + b; }

In addition, we can allow strings with channel values as percentages by adding the loop after redefining rgb. It'll strip the %s and turn what’s left into values out of 255.

function RGBToHex(rgb) { let sep = rgb.indexOf(",") > -1 ? "," : " "; rgb = rgb.substr(4).split(")")[0].split(sep); // Convert %s to 0–255 for (let R in rgb) { let r = rgb[R]; if (r.indexOf("%") > -1) rgb[R] = Math.round(r.substr(0,r.length - 1) / 100 * 255); /* Example: 75% -> 191 75/100 = 0.75, * 255 = 191.25 -> 191 */ } ... }

Now we can supply values like either of these:

  • rgb(255,25,2)
  • rgb(255 25 2)
  • rgb(50%,30%,10%)
  • rgb(50% 30% 10%)
RGBA to Hex (#rrggbbaa)

Converting RGBA to hex with the #rgba or #rrggbbaa notation follows virtually the same process as the opaque counterpart. Since the alpha (a) is normally a value between 0 and 1, we need to multiply it by 255, round the result, then convert it to hexadecimal.

function RGBAToHexA(r,g,b,a) { r = r.toString(16); g = g.toString(16); b = b.toString(16); a = Math.round(a * 255).toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; if (a.length == 1) a = "0" + a; return "#" + r + g + b + a; }

To do this with one string (including with percentages), we can follow what we did earlier. Also note the extra step of splicing out a slash. Since CSS Colors Level 4 supports the syntax of rgba(r g b / a), this is where we allow it. Alpha values can now be percentages! This removes the 0-1-only shackles we used to have. Therefore, the for loop cycling through rgba shall include a part to wipe the % from the alpha without multiplying by 255 (when R is 3 for alpha). Soon we can use values like rgba(255 128 0 / 0.8) and rgba(100% 21% 100% / 30%)!

function RGBAToHexA(rgba) { let sep = rgba.indexOf(",") > -1 ? "," : " "; rgba = rgba.substr(5).split(")")[0].split(sep); // Strip the slash if using space-separated syntax if (rgba.indexOf("/") > -1) rgba.splice(3,1); for (let R in rgba) { let r = rgba[R]; if (r.indexOf("%") > -1) { let p = r.substr(0,r.length - 1) / 100; if (R < 3) { rgba[R] = Math.round(p * 255); } else { rgba[R] = p; } } } }

Then, where the channels are converted to hex, we adjust a to use an item of rgba[].

function RGBAToHexA(rgba) { ... let r = (+rgba[0]).toString(16), g = (+rgba[1]).toString(16), b = (+rgba[2]).toString(16), a = Math.round(+rgba[3] * 255).toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; if (a.length == 1) a = "0" + a; return "#" + r + g + b + a; }

Now the function supports the following:

  • rgba(255,25,2,0.5)
  • rgba(255 25 2 / 0.5)
  • rgba(50%,30%,10%,0.5)
  • rgba(50%,30%,10%,50%)
  • rgba(50% 30% 10% / 0.5)
  • rgba(50% 30% 10% / 50%)
Hex to RGB

We know that the length of hex values must either be 3 or 6 (plus #). In either case, we begin each red (r), green (g), and blue (b) value with "0x" to convert them to hex. If we provide a 3-digit value, we concatenate the same value twice for each channel. If it’s a 6-digit value, we concatenate the first two for red, next two for green, and last two for blue. To get the values for the final rgb() string, we prepend the variables with + to convert them from strings back to numbers, which will yield the decimals we need.

function hexToRGB(h) { let r = 0, g = 0, b = 0; // 3 digits if (h.length == 4) { r = "0x" + h[1] + h[1]; g = "0x" + h[2] + h[2]; b = "0x" + h[3] + h[3]; // 6 digits } else if (h.length == 7) { r = "0x" + h[1] + h[2]; g = "0x" + h[3] + h[4]; b = "0x" + h[5] + h[6]; } return "rgb("+ +r + "," + +g + "," + +b + ")"; } Output RGB with %s

If we want to return rgb() using percentages, then we can modify the function to utilize an optional isPct parameter like so:

function hexToRGB(h,isPct) { let r = 0, g = 0, b = 0; isPct = isPct === true; if (h.length == 4) { r = "0x" + h[1] + h[1]; g = "0x" + h[2] + h[2]; b = "0x" + h[3] + h[3]; } else if (h.length == 7) { r = "0x" + h[1] + h[2]; g = "0x" + h[3] + h[4]; b = "0x" + h[5] + h[6]; } if (isPct) { r = +(r / 255 * 100).toFixed(1); g = +(g / 255 * 100).toFixed(1); b = +(b / 255 * 100).toFixed(1); } return "rgb(" + (isPct ? r + "%," + g + "%," + b + "%" : +r + "," + +g + "," + +b) + ")"; }

Under the last if statement, using +s will convert r, g, and b to numbers. Each toFixed(1) along with them will round the result to the nearest tenth. Additionally, we won’t have whole numbers with .0 or the decades old quirk that produces numbers like 0.30000000000000004. Therefore, in the return, we omitted the +s right before the first r, g, and b to prevent NaNs caused by the %s. Now we can use hexToRGB("#ff0",true) to get rgb(100%,100%,0%)!

Hex (#rrggbbaa) to RGBA

The procedure for hex values with alpha should again be similar with the last. We simply detect a 4- or 8-digit value (plus #) then convert the alpha and divide it by 255. To get more precise output but not long decimal numbers for alpha, we can use toFixed(3).

function hexAToRGBA(h) { let r = 0, g = 0, b = 0, a = 1; if (h.length == 5) { r = "0x" + h[1] + h[1]; g = "0x" + h[2] + h[2]; b = "0x" + h[3] + h[3]; a = "0x" + h[4] + h[4]; } else if (h.length == 9) { r = "0x" + h[1] + h[2]; g = "0x" + h[3] + h[4]; b = "0x" + h[5] + h[6]; a = "0x" + h[7] + h[8]; } a = +(a / 255).toFixed(3); return "rgba(" + +r + "," + +g + "," + +b + "," + a + ")"; } Output RGBA with %s

For a version that outputs percentages, we can do what we did in hexToRGB()—switch r, g, and b to 0–100% when isPct is true.

function hexAToRGBA(h,isPct) { let r = 0, g = 0, b = 0, a = 1; isPct = isPct === true; // Handling of digits ... if (isPct) { r = +(r / 255 * 100).toFixed(1); g = +(g / 255 * 100).toFixed(1); b = +(b / 255 * 100).toFixed(1); } a = +(a / 255).toFixed(3); return "rgba(" + (isPct ? r + "%," + g + "%," + b + "%," + a : +r + "," + +g + "," + +b + "," + a) + ")"; }

Here’s a quick fix if the alpha ought to be a percentage, too: move the statement where a is redefined above the last if statement. Then in that statement, modify a to be like r, g, and b. When isPct is true, a must also gain the %.

function hexAToRGBA(h,isPct) { ... a = +(a / 255).toFixed(3); if (isPct) { r = +(r / 255 * 100).toFixed(1); g = +(g / 255 * 100).toFixed(1); b = +(b / 255 * 100).toFixed(1); a = +(a * 100).toFixed(1); } return "rgba(" + (isPct ? r + "%," + g + "%," + b + "%," + a + "%" : +r + "," + +g + "," + +b + "," + a) + ")"; }

When we enter #7f7fff80 now, we should get rgba(127,127,255,0.502) or rgba(49.8%,49.8%,100%,50.2%).

RGB to HSL

Obtaining HSL values from RGB or hex is a bit more challenging because there’s a larger formula involved. First, we must divide the red, green, and blue by 255 to use values between 0 and 1. Then we find the minimum and maximum of those values (cmin and cmax) as well as the difference between them (delta). We need that result as part of calculating the hue and saturation. Right after the delta, let’s initialize the hue (h), saturation (s), and lightness (l).

function RGBToHSL(r,g,b) { // Make r, g, and b fractions of 1 r /= 255; g /= 255; b /= 255; // Find greatest and smallest channel values let cmin = Math.min(r,g,b), cmax = Math.max(r,g,b), delta = cmax - cmin, h = 0, s = 0, l = 0; }

Next, we need to calculate the hue, which is to be determined by the greatest channel value in cmax (or if all channels are the same). If there is no difference between the channels, the hue will be 0. If cmax is the red, then the formula will be ((g - b) / delta) % 6. If green, then (b - r) / delta + 2. Then, if blue, (r - g) / delta + 4. Finally, multiply the result by 60 (to get the degree value) and round it. Since hues shouldn’t be negative, we add 360 to it, if needed.

function RGBToHSL(r,g,b) { ... // Calculate hue // No difference if (delta == 0) h = 0; // Red is max else if (cmax == r) h = ((g - b) / delta) % 6; // Green is max else if (cmax == g) h = (b - r) / delta + 2; // Blue is max else h = (r - g) / delta + 4; h = Math.round(h * 60); // Make negative hues positive behind 360° if (h < 0) h += 360; }

All that’s left is the saturation and lightness. Let’s calculate the lightness before we do the saturation, as the saturation will depend on it. It’s the sum of the maximum and minimum channel values cut in half ((cmax + cmin) / 2). Then delta will determine what the saturation will be. If it’s 0 (no difference between cmax and cmin), then the saturation is automatically 0. Otherwise, it’ll be 1 minus the absolute value of twice the lightness minus 1 (1 - Math.abs(2 * l - 1)). Once we have these values, we must convert them to values out of 100%, so we multiply them by 100 and round to the nearest tenth. Now we can string together our hsl().

function RGBToHSL(r,g,b) { ... // Calculate lightness l = (cmax + cmin) / 2; // Calculate saturation s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); // Multiply l and s by 100 s = +(s * 100).toFixed(1); l = +(l * 100).toFixed(1); return "hsl(" + h + "," + s + "%," + l + "%)"; } RGB in String

For one string, split the argument by comma or space, strip the %s, and localize r, g, and b like we did before.

function RGBToHSL(rgb) { let sep = rgb.indexOf(",") > -1 ? "," : " "; rgb = rgb.substr(4).split(")")[0].split(sep); for (let R in rgb) { let r = rgb[R]; if (r.indexOf("%") > -1) rgb[R] = Math.round(r.substr(0,r.length - 1) / 100 * 255); } // Make r, g, and b fractions of 1 let r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; ... } RGBA to HSLA

Compared to what we just did to convert RGB to HSL, the alpha counterpart will be basically nothing! We just reuse the code for RGB to HSL (the multi-argument version), leave a alone, and pass a to the returned HSLA. Keep in mind it should be between 0 and 1.

function RGBAToHSLA(r,g,b,a) { // Code for RGBToHSL(r,g,b) before return ... return "hsla(" + h + "," + s + "%," +l + "%," + a + ")"; } RGBA in String

For string values, we apply the splitting and stripping logic again but use the fourth item in rgba for a. Remember the new rgba(r g b / a) syntax? We’re employing the acceptance of it as we did for RGBAToHexA(). Then the rest of the code is the normal RGB-to-HSL conversion.

function RGBAToHSLA(rgba) { let sep = rgba.indexOf(",") > -1 ? "," : " "; rgba = rgba.substr(5).split(")")[0].split(sep); // Strip the slash if using space-separated syntax if (rgba.indexOf("/") > -1) rgba.splice(3,1); for (let R in rgba) { let r = rgba[R]; if (r.indexOf("%") > -1) { let p = r.substr(0,r.length - 1) / 100; if (R < 3) { rgba[R] = Math.round(p * 255); } else { rgba[R] = p; } } } // Make r, g, and b fractions of 1 let r = rgba[0] / 255, g = rgba[1] / 255, b = rgba[2] / 255, a = rgba[3]; // Rest of RGB-to-HSL logic ... }

Wish to leave the alpha as is? Remove the else statement from the for loop.

for (let R in rgba) { let r = rgba[R]; if (r.indexOf("%") > -1) { let p = r.substr(0,r.length - 1) / 100; if (R < 3) { rgba[R] = Math.round(p * 255); } } } HSL to RGB

It takes slightly less logic to convert HSL back to RGB than the opposite way. Since we’ll use a range of 0–100 for the saturation and lightness, the first step is to divide them by 100 to values between 0 and 1. Next, we find chroma (c), which is color intensity, so that’s (1 - Math.abs(2 * l - 1)) * s. Then we use x for the second largest component (first being chroma), the amount to add to each channel to match the lightness (m), and initialize r, g, b.

function HSLToRGB(h,s,l) { // Must be fractions of 1 s /= 100; l /= 100; let c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs((h / 60) % 2 - 1)), m = l - c/2, r = 0, g = 0, b = 0; }

The hue will determine what the red, green, and blue should be depending on which 60° sector of the color wheel it lies.

The color wheel divided into 60° segments

Then c and x shall be assigned as shown below, leaving one channel at 0. To get the final RGB value, we add m to each channel, multiply it by 255, and round it.

function HSLToRGB(h,s,l) { ... if (0 <= h && h < 60) { r = c; g = x; b = 0; } else if (60 <= h && h < 120) { r = x; g = c; b = 0; } else if (120 <= h && h < 180) { r = 0; g = c; b = x; } else if (180 <= h && h < 240) { r = 0; g = x; b = c; } else if (240 <= h && h < 300) { r = x; g = 0; b = c; } else if (300 <= h && h < 360) { r = c; g = 0; b = x; } r = Math.round((r + m) * 255); g = Math.round((g + m) * 255); b = Math.round((b + m) * 255); return "rgb(" + r + "," + g + "," + b + ")"; } HSL in String

For the single string version, we modify the first few statements basically the same way we did for RGBToHSL(r,g,b). Remove s /= 100; and l /= 100; and we’ll use the new statements to wipe the first 4 characters and the ) for our array of HSL values, then the %s from s and l before dividing them by 100.

function HSLToRGB(hsl) { let sep = hsl.indexOf(",") > -1 ? "," : " "; hsl = hsl.substr(4).split(")")[0].split(sep); let h = hsl[0], s = hsl[1].substr(0,hsl[1].length - 1) / 100, l = hsl[2].substr(0,hsl[2].length - 1) / 100; ... }

The next handful of statements shall handle hues provided with a unit—degrees, radians, or turns. We multiply radians by 180/? and turns by 360. If the result ends up over 360, we compound modulus divide to keep it within the scope. All of this will happen before we deal with c, x, and m.

function HSLToRGB(hsl) { ... // Strip label and convert to degrees (if necessary) if (h.indexOf("deg") > -1) h = h.substr(0,h.length - 3); else if (h.indexOf("rad") > -1) h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI)); else if (h.indexOf("turn") > -1) h = Math.round(h.substr(0,h.length - 4) * 360); // Keep hue fraction of 360 if ending up over if (h >= 360) h %= 360; // Conversion to RGB begins ... }

After implementing the steps above, now the following can be safely used:

  • hsl(180 100% 50%)
  • hsl(180deg,100%,50%)
  • hsl(180deg 100% 50%)
  • hsl(3.14rad,100%,50%)
  • hsl(3.14rad 100% 50%)
  • hsl(0.5turn,100%,50%)
  • hsl(0.5turn 100% 50%)

Whew, that’s quite the flexibility!

Output RGB with %s

Similarly, we can modify this function to return percent values just like we did in hexToRGB().

function HSLToRGB(hsl,isPct) { let sep = hsl.indexOf(",") > -1 ? "," : " "; hsl = hsl.substr(4).split(")")[0].split(sep); isPct = isPct === true; ... if (isPct) { r = +(r / 255 * 100).toFixed(1); g = +(g / 255 * 100).toFixed(1); b = +(b / 255 * 100).toFixed(1); } return "rgb("+ (isPct ? r + "%," + g + "%," + b + "%" : +r + "," + +g + "," + +b) + ")"; } HSLA to RGBA

Once again, handling alphas will be a no-brainer. We can reapply the code for the original HSLToRGB(h,s,l) and add a to the return.

function HSLAToRGBA(h,s,l,a) { // Code for HSLToRGB(h,s,l) before return ... return "rgba(" + r + "," + g + "," + b + "," + a + ")"; } HSLA in String

Changing it to one argument, the way we’ll handle strings here will be not too much different than what we did earlier. A new HSLA syntax from Colors Level 4 uses (value value value / value) just like RGBA, so having the code to handle it, we’ll be able to plug in something like hsla(210 100% 50% / 0.5) here.

function HSLAToRGBA(hsla) { let sep = hsla.indexOf(",") > -1 ? "," : " "; hsla = hsla.substr(5).split(")")[0].split(sep); if (hsla.indexOf("/") > -1) hsla.splice(3,1); let h = hsla[0], s = hsla[1].substr(0,hsla[1].length - 1) / 100, l = hsla[2].substr(0,hsla[2].length - 1) / 100, a = hsla[3]; if (h.indexOf("deg") > -1) h = h.substr(0,h.length - 3); else if (h.indexOf("rad") > -1) h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI)); else if (h.indexOf("turn") > -1) h = Math.round(h.substr(0,h.length - 4) * 360); if (h >= 360) h %= 360; ... }

Furthermore, these other combinations have become possible:

  • hsla(180,100%,50%,50%)
  • hsla(180 100% 50% / 50%)
  • hsla(180deg,100%,50%,0.5)
  • hsla(3.14rad,100%,50%,0.5)
  • hsla(0.5turn 100% 50% / 50%)
RGBA with %s

Then we can replicate the same logic for outputting percentages, including alpha. If the alpha should be a percentage (searched in pctFound), here’s how we can handle it:

  1. If r, g, and b are to be converted to percentages, then a should be multiplied by 100, if not already a percentage. Otherwise, drop the %, and it’ll be added back in the return.
  2. If r, g, and b should be left alone, then remove the % from a and divide a by 100.
function HSLAToRGBA(hsla,isPct) { // Code up to slash stripping ... isPct = isPct === true; // h, s, l, a defined to rounding of r, g, b ... let pctFound = a.indexOf("%") > -1; if (isPct) { r = +(r / 255 * 100).toFixed(1); g = +(g / 255 * 100).toFixed(1); b = +(b / 255 * 100).toFixed(1); if (!pctFound) { a *= 100; } else { a = a.substr(0,a.length - 1); } } else if (pctFound) { a = a.substr(0,a.length - 1) / 100; } return "rgba("+ (isPct ? r + "%," + g + "%," + b + "%," + a + "%" : +r + ","+ +g + "," + +b + "," + +a) + ")"; } Hex to HSL

You might think this one and the next are crazier processes than the others, but they merely come in two parts with recycled logic. First, we convert the hex to RGB. That gives us the base 10s we need to convert to HSL.

function hexToHSL(H) { // Convert hex to RGB first let r = 0, g = 0, b = 0; if (H.length == 4) { r = "0x" + H[1] + H[1]; g = "0x" + H[2] + H[2]; b = "0x" + H[3] + H[3]; } else if (H.length == 7) { r = "0x" + H[1] + H[2]; g = "0x" + H[3] + H[4]; b = "0x" + H[5] + H[6]; } // Then to HSL r /= 255; g /= 255; b /= 255; let cmin = Math.min(r,g,b), cmax = Math.max(r,g,b), delta = cmax - cmin, h = 0, s = 0, l = 0; if (delta == 0) h = 0; else if (cmax == r) h = ((g - b) / delta) % 6; else if (cmax == g) h = (b - r) / delta + 2; else h = (r - g) / delta + 4; h = Math.round(h * 60); if (h < 0) h += 360; l = (cmax + cmin) / 2; s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); s = +(s * 100).toFixed(1); l = +(l * 100).toFixed(1); return "hsl(" + h + "," + s + "%," + l + "%)"; } Hex (#rrggbbaa) to HSLA

There aren’t too many lines that change in this one. We’ll repeat what we recently did to get the alpha by converting the hex, but won’t divide it by 255 right away. First, we must get the hue, saturation, and lightness as we did in the other to-HSL functions. Then, before the ending return, we divide the alpha and set the decimal places.

function hexAToHSLA(H) { let r = 0, g = 0, b = 0, a = 1; if (H.length == 5) { r = "0x" + H[1] + H[1]; g = "0x" + H[2] + H[2]; b = "0x" + H[3] + H[3]; a = "0x" + H[4] + H[4]; } else if (H.length == 9) { r = "0x" + H[1] + H[2]; g = "0x" + H[3] + H[4]; b = "0x" + H[5] + H[6]; a = "0x" + H[7] + H[8]; } // Normal conversion to HSL ... a = (a / 255).toFixed(3); return "hsla("+ h + "," + s + "%," + l + "%," + a + ")"; } HSL to Hex

This one starts as a conversion to RGB, but there’s an extra step to the Math.round()s of converting the RGB results to hex.

function HSLToHex(h,s,l) { s /= 100; l /= 100; let c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs((h / 60) % 2 - 1)), m = l - c/2, r = 0, g = 0, b = 0; if (0 <= h && h < 60) { r = c; g = x; b = 0; } else if (60 <= h && h < 120) { r = x; g = c; b = 0; } else if (120 <= h && h < 180) { r = 0; g = c; b = x; } else if (180 <= h && h < 240) { r = 0; g = x; b = c; } else if (240 <= h && h < 300) { r = x; g = 0; b = c; } else if (300 <= h && h < 360) { r = c; g = 0; b = x; } // Having obtained RGB, convert channels to hex r = Math.round((r + m) * 255).toString(16); g = Math.round((g + m) * 255).toString(16); b = Math.round((b + m) * 255).toString(16); // Prepend 0s, if necessary if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; return "#" + r + g + b; } HSL in String

Even the first few lines of this function will be like those in HSLToRGB() if we changed it to accept a single string. This is how we’ve been obtaining the hue, saturation, and lightness separately in the first place. Let’s not forget the step to remove the hue label and convert to degrees, too. All of this will be in place of s /= 100; and l /= 100;.

function HSLToHex(hsl) { let sep = hsl.indexOf(",") > -1 ? "," : " "; hsl = hsl.substr(4).split(")")[0].split(sep); let h = hsl[0], s = hsl[1].substr(0,hsl[1].length - 1) / 100, l = hsl[2].substr(0,hsl[2].length - 1) / 100; // Strip label and convert to degrees (if necessary) if (h.indexOf("deg") > -1) h = h.substr(0,h.length - 3); else if (h.indexOf("rad") > -1) h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI)); else if (h.indexOf("turn") > -1) h = Math.round(h.substr(0,h.length - 4) * 360); if (h >= 360) h %= 360; ... } HSLA to Hex (#rrggbbaa)

Adding alpha to the mix, we convert a to hex and add a fourth if to prepend a 0, if necessary. You probably already familiar with this logic because we last used it in RGBAToHexA().

function HSLAToHexA(h,s,l,a) { // Repeat code from HSLToHex(h,s,l) until 3 `toString(16)`s ... a = Math.round(a * 255).toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; if (a.length == 1) a = "0" + a; return "#" + r + g + b + a; } HSLA in String

Finally, the lines of the single argument version up to a = hsla[3] are no different than those of HSLAToRGBA().

function HSLAToHexA(hsla) { let sep = hsla.indexOf(",") > -1 ? "," : " "; hsla = hsla.substr(5).split(")")[0].split(sep); // Strip the slash if (hsla.indexOf("/") > -1) hsla.splice(3,1); let h = hsla[0], s = hsla[1].substr(0,hsla[1].length - 1) / 100, l = hsla[2].substr(0,hsla[2].length - 1) / 100, a = hsla[3]; ... } Built-in Names

To convert a named color to RGB, hex, or HSL, you might consider turning this table of 140+ names and hex values into a massive object at the start. The truth is that we really don’t need one because here’s what we can do:

  1. Create an element
  2. Give it a text color
  3. Obtain the value of that property
  4. Remove the element
  5. Return the stored color value, which will be in RGB by default

So, our function to get RGB will only be seven statements!

function nameToRGB(name) { // Create fake div let fakeDiv = document.createElement("div"); fakeDiv.style.color = name; document.body.appendChild(fakeDiv); // Get color of div let cs = window.getComputedStyle(fakeDiv), pv = cs.getPropertyValue("color"); // Remove div after obtaining desired color value document.body.removeChild(fakeDiv); return pv; }

Let’s go even further. How about we change the output to hex instead?

function nameToHex(name) { // Get RGB from named color in temporary div let fakeDiv = document.createElement("div"); fakeDiv.style.color = name; document.body.appendChild(fakeDiv); let cs = window.getComputedStyle(fakeDiv), pv = cs.getPropertyValue("color"); document.body.removeChild(fakeDiv); // Code ripped from RGBToHex() (except pv is substringed) let rgb = pv.substr(4).split(")")[0].split(","), r = (+rgb[0]).toString(16), g = (+rgb[1]).toString(16), b = (+rgb[2]).toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; return "#" + r + g + b; }

Or, why not HSL? &#x1f609;

function nameToHSL(name) { let fakeDiv = document.createElement("div"); fakeDiv.style.color = name; document.body.appendChild(fakeDiv); let cs = window.getComputedStyle(fakeDiv), pv = cs.getPropertyValue("color"); document.body.removeChild(fakeDiv); // Code ripped from RGBToHSL() (except pv is substringed) let rgb = pv.substr(4).split(")")[0].split(","), r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, cmin = Math.min(r,g,b), cmax = Math.max(r,g,b), delta = cmax - cmin, h = 0, s = 0, l = 0; if (delta == 0) h = 0; else if (cmax == r) h = ((g - b) / delta) % 6; else if (cmax == g) h = (b - r) / delta + 2; else h = (r - g) / delta + 4; h = Math.round(h * 60); if (h < 0) h += 360; l = (cmax + cmin) / 2; s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); s = +(s * 100).toFixed(1); l = +(l * 100).toFixed(1); return "hsl(" + h + "," + s + "%," + l + "%)"; }

In the long run, every conversion from a name becomes a conversion from RGB after cracking the name.

Validating Colors

In all these functions, there haven’t been any measures to prevent or correct ludicrous input (say hues over 360 or percentages over 100). If we’re only manipulating pixels on a <canvas> fetched using getImageData(), validation of color values isn’t necessary before converting because they’ll be correct no matter what. If we’re creating a color conversion tool where users supply the color, then validation would be much needed.

It’s easy to handle improper input for channels as separate arguments, like this for RGB:

// Correct red if (r > 255) r = 255; else if (r < 0) r = 0;

If validating a whole string, then a regular expression is needed. For instance, this is the RGBToHex() function given a validation step with an expression:

function RGBToHex(rgb) { // Expression for rgb() syntaxes let ex = /^rgb\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){2}|((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s)){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))|((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%))\)$/i; if (ex.test(rgb)) { // Logic to convert RGB to hex ... } else { // Something to do if color is invalid } }

To test other types of values, below is a table of expressions to cover both opaque and alpha-enabled:

Color Value RegEx RGB /^rgb\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){2}|((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s)){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))|((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%))\)$/i RGBA /^rgba\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3}))|(((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){3}))\/\s)((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i Hex /^#([\da-f]{3}){1,2}$/i Hex (with Alpha) /^#([\da-f]{4}){1,2}$/i HSL /^hsl\(((((([12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6](\.\d+)?)|(\.\d+))rad)((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}|(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2})\)$/i HSLA /^hsla\(((((([12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6](\.\d+)?)|(\.\d+))rad)(((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)|((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i

Looking at the expressions for RGB(A) and HSL(A), you probably have big eyes right now; these were made comprehensive enough to include most of the new syntaxes from CSS Colors Level 4. Hex, on the other hand, doesn’t need expressions as long as the others because of only digit counts. In a moment, we’ll dissect these and decipher the parts. Note that case-insensitive values (/i) pass all these.

RGB /^rgb\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){2}|((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s)){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))|((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%))\)$/i

Because rgb() accepts either all integers or all percentages, both cases are covered. In the outmost group, between the ^rgb\( and \)$, there are inner groups for both integers and percentages, all comma-spaces or spaces only as separators:

  1. (((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?){2}|(((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))
  2. ((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)

In the first half, we accept two instances of integers for red and green from 0–99 or 111-199 ((1?[1-9]?\d)), 100–109 (10\d), 200-249 ((2[0-4]\d)), or 250–255 (25[0-5]). We couldn’t simply do \d{1,3} because values like 03 or 017 and those greater than 255 shouldn’t be allowed. After that goes the comma and optional space (,\s?). On the other side of the |, after the first {2} (which indicates two instances of integers), we check for the same thing with space separators if the left side is false. Then for blue, the same should be accepted, but without a separator.

In the other half, acceptable values for percentages, including floats, should either be 0–99, explicitly 100 and not a float, or floats under 1 with the 0 dropped. Therefore, the segment here is (([1-9]?\d(\.\d+)?)|100|(\.\d+)), and it appears three times; twice with separator (,\s?){2}, %\s){2}), once without.

It is legal to use percentages without space separators (rgb(100%50%10%) for instance) in CSS, but the functions we wrote don’t support that. The same goes for rgba(100%50%10%/50%), hsl(40 100%50%), and hsla(40 100%50%/0.5). This could very well be a plus for code golfing and minification!

RGBA /^rgba\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3}))|(((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){3}))\/\s)((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i

The next expression is very similar to the pervious, but three instances of integers (((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?){3})) or percentages ((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3})), plus comma optional space are checked. Otherwise, it looks for the same thing but with space separators, plus a slash and space (\/\s) after the blue. Next to that is ((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%) where we accept floats with or without the first 0 ((0?\.\d+)), 0 or 1 ([01]) on the dot, or 0–100% ((([1-9]?\d(\.\d+)?)|100|(\.\d+))%).

Hex with Alpha // #rgb/#rrggbb /^#([\da-f]{3}){1,2}$/i // #rgba/#rrggbbaa /^#([\da-f]{4}){1,2}$/i

For both hex—with and without alpha—instances of numbers or letters a–f ([\da-f]) are accepted. Then one or two instances of this are counted for either short or longhand values supplied (#rgb or #rrggbb). As an illustration, we have this same short pattern: /^#([\da-f]{n}){1,2}$/i. Simply change n to 3 or 4.

HSL and HSLA // HSL /^hsl\((((((\[12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6\\.\d+)?)|(\.\d+))rad)((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}|(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2})\)$/i // HSLA /^hsla\((((((\[12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6\\.\d+)?)|(\.\d+))rad)(((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)|((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i

After the \( in both expressions for HSL and HSLA, this large chunk is for the hue:

(((((\[12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6\\.\d+)?)|(\.\d+))rad)

([12]?[1-9]?\d) covers 0–99, 110–199, and 210–299. [12]0\d covers 110–109 and 200–209. Then (3[0-5]\d) takes care of 300–359. The reason for this division of ranges is similar to that of integers in the rgb() syntax: ruling out zeros coming first and values greater than the maximum. Since hues can be floating point numbers, the first (\.\d+)? is for that.

Next to the | after the aforementioned segment of code, the second (\.\d+) is for floats without a leading zero.

Now let’s move up a level and decipher the next small chunk:

(deg)?|(0|0?\.\d+)turn|((\[0-6\\.\d+)?)|(\.\d+))rad

This contains the labels we can use for the hue—degrees, turns, or radians. We can include all or none of deg. Values in turn must be under 1. For radians, we can accept any float between 0–7. We do know, however, that one 360° turn is 2?, and it stops approximately at 6.28. You may think 6.3 and over shouldn’t be accepted. Because 2? is an irrational number, it would be too messy for this example to try to satisfy every decimal place provided by the JavaScript console. Besides, we have this snippet in our HSLTo_() functions as a second layer of security if hues 360° or over were to happen:

// Keep hue fraction of 360 if ending up over if (h >= 360) h %= 360;

Now let’s move up a level and decipher the second chunk:

(,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}

We’re counting two instances of comma-space-percentages for the saturation and lightness (space optional). In the group after the ,\s?, we test for values 0–99 with or without decimal points (([1-9]?\d(\.\d+)?)), exactly 100, or floats under 1 without the leading 0 ((\.\d+)).

The last part the HSL expression, before the ending (\)$/i), is a similar expression if spaces are the only separator:

(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}

\s is in the beginning instead of ,\s?. Then in the HSLA expression, this same chunk is inside another group with ,\s? after its {2}.

((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)

That counts the comma-space between the lightness and alpha. Then if we have spaces as separators, we need to check for a space-slash-space (\s\/\s) after counting two instances of space and a percentage.

((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))

After that, we have this left to check the alpha value:

(((0?\.\d+)|[01])|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)

Matches for (0?\.\d+) include floats under 1 with or without the leading 0, 0 or 1 for [01], and 0–100%.

Conclusion

If your current challenge is to convert one color space to another, you now have some ideas on how to approach it. Because it would be tiresome to walk through converting every color space ever invented in one post, we discussed the most practical and browser-supported ones. If you’d like to go beyond supported color spaces (say CMYK, XYZ, or CIE L*a*b*), EasyRGB) provides an amazing set of code-ready formulas.

To see all the conversions demonstrated here, I’ve set up a CodePen demo that shows inputs and outputs in a table. You can try different colors in lines 2–10 and see the complete functions in the JavaScript panel.

See the Pen Color Conversion by Jon Kantner (@jkantner) on CodePen.

The post Converting Color Spaces in JavaScript appeared first on CSS-Tricks.

Algorithmic Layouts

Css Tricks - Thu, 01/10/2019 - 5:06am

Don't miss this video by Heydon that digs into CSS layouts. It's great how he combines fundamental knowledge, like the way elements flow, wrap, and can have margin with new layout methods like flexbox and grid (with specific examples). Of particular note is the clear demonstration of how flexbox and grid help avoid the need to constantly intervene with media queries in order to affect responsive layouts.

So, in place of this...

.sidebar { float: left; width: 20rem; } .not-sidebar { float-right: calc(100% - 20rem); } @media (max-width: 40rem) { .sidebar, .not-sidebar { float: none; width: auto. } }

...something like this:

/* Parent container */ .with-sidebar { display: flex; flex-wrap: wrap; } .sidebar { flex-basis: 20rem; flex-grow: 1; } .not-sidebar { min-width: 50%; flex-grow: 600; }

This isn't a one-off video either, Heydon's channel has videos on making unusual shapes and custom properties as well.

Direct Link to ArticlePermalink

The post Algorithmic Layouts appeared first on CSS-Tricks.

Building Responsive WordPress Forms

Css Tricks - Thu, 01/10/2019 - 5:04am

Within the arsenal of every WordPress developer exists a toolbox of plugins used to implement key features on a website. Forms, up until now, have been a point of contention for most developers, given that no form plugins have offered seamless integration with existing website code. Therefore, forms often become an alien chunk of code requiring custom and time-consuming stylization.

Now there’s a solution: WS Form

WS Form is a developer-focused WordPress form plugin, which outputs framework-ready, responsive HTML5 code. It allows you to rapidly create forms using an innovative layout editor and a plethora of development features.

Front-End, Framework-Compatible HTML from a Layout Editor

If you’re developing or implementing a theme using Bootstrap (versions 3 & 4) or Foundation (versions 5, 6 & 6.4+), WS Form will output code that is native to those frameworks. For themes that do not use those frameworks, a fallback framework is included that is fully responsive and easy for developers to style.

The WS Form layout editor allows you to edit your form at any breakpoint. Form elements are dragged and dropped into the form, and all responsive CSS classes are handled for you. For developers wanting additional control, each field type comes with a vast array of settings, including the ability to add your own wrapper and field-level classes.

And within WS Form, time travel is real. The undo history feature allows you to step back to any point in your form development and continue from that point forward.

Introducing the First Form Debug Console

WS Form is the first WordPress form plugin to offer a dedicated debug console for developers.

A time-consuming task, when developing any form, is having to repeatedly populate a form to test it. WS Form is the first WordPress form plugin to offer the ability to automatically populate a form. Simply click "Populate" in the debug console, and the form will be pre-populated with different sample data each time. This dramatically speeds up development time, particularly with larger, multi-tab forms.

The console provides per form instance activity and error logging, as well as the ability to reload a form while still on the same web page.

Extensive HTML5 Input Type Support

WS Form includes settings for all form input types. Settings include everything from default values and placeholder text to custom validation messages and datalists. In addition to elementary HTML5 input types, WS Form offers additional fields, such as reCAPTCHA, signatures, and even e-commerce and payment buttons.

Some HTML5 input types, such as date and color selectors, are still not supported in all web browsers. WS Form overcomes this obstacle by checking for native support, and if unavailable, a suitable alternative component is loaded. You have the option of loading that component from your web server or from a CDN.

See the Field Types

Limitless Conditional Logic

Conditional logic allows you to make a form interactive and improve usability. For example, you could opt to only show shipping address fields if a checkbox is checked, or you could show an error message if a password confirmation does not match.

WS Form comes with an extensive array of options when creating if, then, and else conditions at form, tab, section, and field levels. Furthermore, conditional options are context sensitive, so, for example, color fields allow you to fire behavior if the hue or lightness of that field matches specified conditions. WS Form even allows you to fire actions, such as sending an email or showing a message, if any condition is met. This could be useful for automatically saving a form as a user steps through tabs on a form.

An Ever-Expanding Library of Form Actions

WS Form actions are fired whenever a form is saved or submitted by a user. Actions can also be fired using conditional logic.

The actions include:

  • Sending emails
  • Showing messages (e.g., a thank you message)
  • Running JavaScript
  • Firing a WordPress hook (actions or filters)
  • Initiating WordPress GDPR functionality, such as a data export or erasure request
  • Redirecting

See the Actions

Try it Today

Building a WordPress form in WS Form means you can rapidly prototype and implement forms. With responsive HTML5 code, automatic framework compatibility, and advanced conditional logic, just to name a few of the features, WS Form is changing the way WordPress forms can enhance and empower a website.

Use coupon code CSST20 to receive 20% off any WS Form PRO product!

Try a Demo

The post Building Responsive WordPress Forms appeared first on CSS-Tricks.

New ES2018 Features Every JavaScript Developer Should Know

Css Tricks - Wed, 01/09/2019 - 5:19am

The ninth edition of the ECMAScript standard, officially known as ECMAScript 2018 (or ES2018 for short), was released in June 2018. Starting with ES2016, new versions of ECMAScript specifications are released yearly rather than every several years and add fewer features than major editions used to. The newest edition of the standard continues the yearly release cycle by adding four new RegExp features, rest/spread properties, asynchronous iteration, and Promise.prototype.finally. Additionally, ES2018 drops the syntax restriction of escape sequences from tagged templates.

These new changes are explained in the subsections that follow.

The Rest/Spread Properties

One of the most interesting features added to ES2015 was the spread operator. This operator makes copying and merging arrays a lot simpler. Rather than calling the concat() or slice() method, you could use the ... operator:

const arr1 = [10, 20, 30]; // make a copy of arr1 const copy = [...arr1]; console.log(copy); // ? [10, 20, 30] const arr2 = [40, 50]; // merge arr2 with arr1 const merge = [...arr1, ...arr2]; console.log(merge); // ? [10, 20, 30, 40, 50]

The spread operator also comes in handy in situations where an array must be passed in as separate arguments to a function. For example:

const arr = [10, 20, 30] // equivalent to // console.log(Math.max(10, 20, 30)); console.log(Math.max(...arr)); // ? 30

ES2018 further expands this syntax by adding spread properties to object literals. With the spread properties you can copy own enumerable properties of an object onto a new object. Consider the following example:

const obj1 = { a: 10, b: 20 }; const obj2 = { ...obj1, c: 30 }; console.log(obj2); // ? {a: 10, b: 20, c: 30}

In this code, the ... operator is used to retrieve the properties of obj1 and assign them to obj2. Prior to ES2018, attempting to do so would throw an error. If there are multiple properties with the same name, the property that comes last will be used:

const obj1 = { a: 10, b: 20 }; const obj2 = { ...obj1, a: 30 }; console.log(obj2); // ? {a: 30, b: 20}

Spread properties also provide a new way to merge two or more objects, which can be used as an alternative to the Object.assign() method:

const obj1 = {a: 10}; const obj2 = {b: 20}; const obj3 = {c: 30}; // ES2018 console.log({...obj1, ...obj2, ...obj3}); // ? {a: 10, b: 20, c: 30} // ES2015 console.log(Object.assign({}, obj1, obj2, obj3)); // ? {a: 10, b: 20, c: 30}

Note, however, that spread properties do not always produce the same result as Object.assign(). Consider the following code:

Object.defineProperty(Object.prototype, 'a', { set(value) { console.log('set called!'); } }); const obj = {a: 10}; console.log({...obj}); // ? {a: 10} console.log(Object.assign({}, obj)); // ? set called! // ? {}

In this code, the Object.assign() method executes the inherited setter property. Conversely, the spread properties simply ignore the setter.

It's important to remember that spread properties only copy enumerable properties. In the following example, the type property won’t show up in the copied object because its enumerable attribute is set to false:

const car = { color: 'blue' }; Object.defineProperty(car, 'type', { value: 'coupe', enumerable: false }); console.log({...car}); // ? {color: "blue"}

Inherited properties are ignored even if they are enumerable:

const car = { color: 'blue' }; const car2 = Object.create(car, { type: { value: 'coupe', enumerable: true, } }); console.log(car2.color); // ? blue console.log(car2.hasOwnProperty('color')); // ? false console.log(car2.type); // ? coupe console.log(car2.hasOwnProperty('type')); // ? true console.log({...car2}); // ? {type: "coupe"}

In this code, car2 inherits the color property from car. Because spread properties only copy the own properties of an object, color is not included in the return value.

Keep in mind that spread properties can only make a shallow copy of an object. If a property holds an object, only the reference to the object will be copied:

const obj = {x: {y: 10}}; const copy1 = {...obj}; const copy2 = {...obj}; console.log(copy1.x === copy2.x); // ? true

The x property in copy1 refers to the same object in memory that x in copy2 refers to, so the strict equality operator returns true.

Another useful feature added to ES2015 was rest parameters, which enabled JavaScript programmers to use ... to represent values as an array. For example:

const arr = [10, 20, 30]; const [x, ...rest] = arr; console.log(x); // ? 10 console.log(rest); // ? [20, 30]

Here, the first item in arr is assigned to x, and remaining elements are assigned to the rest variable. This pattern, called array destructuring, became so popular that the Ecma Technical Committee decided to bring a similar functionality to objects:

const obj = { a: 10, b: 20, c: 30 }; const {a, ...rest} = obj; console.log(a); // ? 10 console.log(rest); // ? {b: 20, c: 30}

This code uses the rest properties in a destructuring assignment to copy the remaining own enumerable properties into a new object. Note that rest properties must always appear at the end of the object, otherwise an error is thrown:

const obj = { a: 10, b: 20, c: 30 }; const {...rest, a} = obj; // ? SyntaxError: Rest element must be last element

Also keep in mind that using multiple rest syntaxes in an object causes an error, unless they are nested:

const obj = { a: 10, b: { x: 20, y: 30, z: 40 } }; const {b: {x, ...rest1}, ...rest2} = obj; // no error const {...rest, ...rest2} = obj; // ? SyntaxError: Rest element must be last element Support for Rest/Spread Properties Chrome Firefox Safari Edge 60 55 11.1 No Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview 60 55 11.3 No 8.2 60

Node.js:

  • 8.0.0 (requires the --harmony runtime flag)
  • 8.3.0 (full support)
Asynchronous Iteration

Iterating over a collection of data is an important part of programming. Prior to ES2015, JavaScript provided statements such as for, for...in, and while, and methods such as map(), filter(), and forEach() for this purpose. To enable programmers to process the elements in a collection one at a time, ES2015 introduced the iterator interface.

An object is iterable if it has a Symbol.iterator property. In ES2015, strings and collections objects such as Set, Map, and Array come with a Symbol.iterator property and thus are iterable. The following code gives an example of how to access the elements of an iterable one at a time:

const arr = [10, 20, 30]; const iterator = arr[Symbol.iterator](); console.log(iterator.next()); // ? {value: 10, done: false} console.log(iterator.next()); // ? {value: 20, done: false} console.log(iterator.next()); // ? {value: 30, done: false} console.log(iterator.next()); // ? {value: undefined, done: true}

Symbol.iterator is a well-known symbol specifying a function that returns an iterator. The primary way to interact with an iterator is the next() method. This method returns an object with two properties: value and done. The value property contains the value of the next element in the collection. The done property contains either true or false denoting whether or not the end of the collection has reached.

By default, a plain object is not iterable, but it can become iterable if you define a Symbol.iterator property on it, as in this example:

const collection = { a: 10, b: 20, c: 30, [Symbol.iterator]() { const values = Object.keys(this); let i = 0; return { next: () => { return { value: this[values[i++]], done: i > values.length } } }; } }; const iterator = collection[Symbol.iterator](); console.log(iterator.next()); // ? {value: 10, done: false} console.log(iterator.next()); // ? {value: 20, done: false} console.log(iterator.next()); // ? {value: 30, done: false} console.log(iterator.next()); // ? {value: undefined, done: true}

This object is iterable because it defines a Symbol.iterator property. The iterator uses the Object.keys() method to get an array of the object's property names and then assigns it to the values constant. It also defines a counter variable and gives it an initial value of 0. When the iterator is executed it returns an object that contains a next() method. Each time the next() method is called, it returns a {value, done} pair, with value holding the next element in the collection and done holding a Boolean indicating if the iterator has reached the need of the collection.

While this code works perfectly, it’s unnecessarily complicated. Fortunately, using a generator function can considerably simplify the process:

const collection = { a: 10, b: 20, c: 30, [Symbol.iterator]: function * () { for (let key in this) { yield this[key]; } } }; const iterator = collection[Symbol.iterator](); console.log(iterator.next()); // ? {value: 10, done: false} console.log(iterator.next()); // ? {value: 20, done: false} console.log(iterator.next()); // ? {value: 30, done: false} console.log(iterator.next()); // ? {value: undefined, done: true}

Inside this generator, a for...in loop is used to enumerate over the collection and yield the value of each property. The result is exactly the same as the previous example, but it’s greatly shorter.

A downside of iterators is that they are not suitable for representing asynchronous data sources. ES2018’s solution to remedy that is asynchronous iterators and asynchronous iterables. An asynchronous iterator differs from a conventional iterator in that, instead of returning a plain object in the form of {value, done}, it returns a promise that fulfills to {value, done}. An asynchronous iterable defines a Symbol.asyncIterator method (instead of Symbol.iterator) that returns an asynchronous iterator.

An example should make this clearer:

const collection = { a: 10, b: 20, c: 30, [Symbol.asyncIterator]() { const values = Object.keys(this); let i = 0; return { next: () => { return Promise.resolve({ value: this[values[i++]], done: i > values.length }); } }; } }; const iterator = collection[Symbol.asyncIterator](); console.log(iterator.next().then(result => { console.log(result); // ? {value: 10, done: false} })); console.log(iterator.next().then(result => { console.log(result); // ? {value: 20, done: false} })); console.log(iterator.next().then(result => { console.log(result); // ? {value: 30, done: false} })); console.log(iterator.next().then(result => { console.log(result); // ? {value: undefined, done: true} }));

Note that it’s not possible to use an iterator of promises to achieve the same result. Although a normal, synchronous iterator can asynchronously determine the values, it still needs to determine the state of "done" synchronously.

Again, you can simplify the process by using a generator function, as shown below:

const collection = { a: 10, b: 20, c: 30, [Symbol.asyncIterator]: async function * () { for (let key in this) { yield this[key]; } } }; const iterator = collection[Symbol.asyncIterator](); console.log(iterator.next().then(result => { console.log(result); // ? {value: 10, done: false} })); console.log(iterator.next().then(result => { console.log(result); // ? {value: 20, done: false} })); console.log(iterator.next().then(result => { console.log(result); // ? {value: 30, done: false} })); console.log(iterator.next().then(result => { console.log(result); // ? {value: undefined, done: true} }));

Normally, a generator function returns a generator object with a next() method. When next() is called it returns a {value, done} pair whose value property holds the yielded value. An async generator does the same thing except that it returns a promise that fulfills to {value, done}.

An easy way to iterate over an iterable object is to use the for...of statement, but for...of doesn't work with async iterables as value and done are not determined synchronously. For this reason, ES2018 provides the for...await...of statement. Let’s look at an example:

const collection = { a: 10, b: 20, c: 30, [Symbol.asyncIterator]: async function * () { for (let key in this) { yield this[key]; } } }; (async function () { for await (const x of collection) { console.log(x); } })(); // logs: // ? 10 // ? 20 // ? 30

In this code, the for...await...of statement implicitly calls the Symbol.asyncIterator method on the collection object to get an async iterator. Each time through the loop, the next() method of the iterator is called, which returns a promise. Once the promise is resolved, the value property of the resulting object is read to the x variable. The loop continues until the done property of the returned object has a value of true.

Keep in mind that the for...await...of statement is only valid within async generators and async functions. Violating this rule results in a SyntaxError.

The next() method may return a promise that rejects. To gracefully handle a rejected promise, you can wrap the for...await...of statement in a try...catch statement, like this:

const collection = { [Symbol.asyncIterator]() { return { next: () => { return Promise.reject(new Error('Something went wrong.')) } }; } }; (async function() { try { for await (const value of collection) {} } catch (error) { console.log('Caught: ' + error.message); } })(); // logs: // ? Caught: Something went wrong. Support for Asynchronous Iterators Chrome Firefox Safari Edge 63 57 12 No Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview 63 57 12 No 8.2 63

Node.js:

  • 8.10.0 (requires the --harmony_async_iteration flag)
  • 10.0.0 (full support)
Promise.prototype.finally

Another exciting addition to ES2018 is the finally() method. Several JavaScript libraries had previously implemented a similar method, which proved useful in many situations. This encouraged the Ecma Technical Committee to officially add finally() to the specification. With this method, programmers will be able to execute a block of code regardless of the promise's fate. Let’s look at a simple example:

fetch('https://www.google.com') .then((response) => { console.log(response.status); }) .catch((error) => { console.log(error); }) .finally(() => { document.querySelector('#spinner').style.display = 'none'; });

The finally() method comes in handy when you need to do some clean up after the operation has finished regardless of whether or not it succeeded. In this code, the finally() method simply hides the loading spinner after the data is fetched and processed. Instead of duplicating the final logic in the then() and catch() methods, the code registers a function to be executed once the promise is either fulfilled or rejected.

You could achieve the same result by using promise.then(func, func) rather than promise.finally(func), but you would have to repeat the same code in both fulfillment handler and rejection handler, or declare a variable for it:

fetch('https://www.google.com') .then((response) => { console.log(response.status); }) .catch((error) => { console.log(error); }) .then(final, final); function final() { document.querySelector('#spinner').style.display = 'none'; }

As with then() and catch(), the finally() method always returns a promise, so you can chain more methods. Normally, you want to use finally() as the last chain, but in certain situations, such as when making a HTTP request, it’s a good practice to chain another catch() to deal with errors that may occur in finally().

Support for Promise.prototype.finally Chrome Firefox Safari Edge 63 58 11.1 18 Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview 63 58 11.1 No 8.2 63

Node.js:

10.0.0 (full support)

New RegExp Features

ES2018 adds four new features to the RegExp object, which further improves JavaScript’s string processing capabilities. These features are as follows:

  • s (dotAll) flag
  • Named capture groups
  • Lookbehind assertions
  • Unicode property escapes
s (dotAll) Flag

The dot (.) is a special character in a regular expression pattern that matches any character except line break characters such as line feed (\n) or carriage return (\r). A workaround to match all characters including line breaks is to use a character class with two opposite shorthands such as [\d\D]. This character class tells the regular expression engine to find a character that’s either a digit (\d) or a non-digit (\D). As a result, it matches any character:

console.log(/one[\d\D]two/.test('one\ntwo')); // ? true

ES2018 introduces a mode in which the dot can be used to achieve the same result. This mode can be activated on per-regex basis by using the s flag:

console.log(/one.two/.test('one\ntwo')); // ? false console.log(/one.two/s.test('one\ntwo')); // ? true

The benefit of using a flag to opt in to the new behavior is backwards compatibility. So existing regular expression patterns that use the dot character are not affected.

Named Capture Groups

In some regular expression patterns, using a number to reference a capture group can be confusing. For example, take the regular expression /(\d{4})-(\d{2})-(\d{2})/ which matches a date. Because date notation in American English is different from British English, it’s hard to know which group refers to the day and which group refers to the month:

const re = /(\d{4})-(\d{2})-(\d{2})/; const match= re.exec('2019-01-10'); console.log(match[0]); // ? 2019-01-10 console.log(match[1]); // ? 2019 console.log(match[2]); // ? 01 console.log(match[3]); // ? 10

ES2018 introduces named capture groups which uses the (?<name>...) syntax. So, the pattern to match a date can be written in a less ambiguous manner:

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const match = re.exec('2019-01-10'); console.log(match.groups); // ? {year: "2019", month: "01", day: "10"} console.log(match.groups.year); // ? 2019 console.log(match.groups.month); // ? 01 console.log(match.groups.day); // ? 10

You can recall a named capture group later in the pattern by using the \k<name> syntax. For example, to find consecutive duplicate words in a sentence, you can use /\b(?<dup>\w+)\s+\k<dup>\b/:

const re = /\b(?<dup>\w+)\s+\k<dup>\b/; const match = re.exec('Get that that cat off the table!'); console.log(match.index); // ? 4 console.log(match[0]); // ? that that

To insert a named capture group into the replacement string of the replace() method, you will need to use the $<name> construct. For example:

const str = 'red & blue'; console.log(str.replace(/(red) & (blue)/, '$2 & $1')); // ? blue & red console.log(str.replace(/(?<red>red) & (?<blue>blue)/, '$<blue> & $<red>')); // ? blue & red Lookbehind Assertions

ES2018 brings lookbehind assertions to JavaScript, which have been available in other regex implementations for years. Previously, JavaScript only supported lookahead assertions. A lookbehind assertion is denoted by (?<=...), and enables you to match a pattern based on the substring that precedes the pattern. For example, if you want to match the price of a product in dollar, pound, or euro without capturing the currency symbol, you can use /(?<=\$|£|€)\d+(\.\d*)?/:

const re = /(?<=\$|£|€)\d+(\.\d*)?/; console.log(re.exec('199')); // ? null console.log(re.exec('$199')); // ? ["199", undefined, index: 1, input: "$199", groups: undefined] console.log(re.exec('€50')); // ? ["50", undefined, index: 1, input: "€50", groups: undefined]

There is also a negative version of lookbehind, which is denoted by (?<!...). A negative lookbehind allows you to match a pattern only if it is not preceded by the pattern within the lookbehind. For example, the pattern /(?<!un)available/ matches the word available if it does not have a "un" prefix:

const re = /(?<!un)available/; console.log(re.exec('We regret this service is currently unavailable')); // ? null console.log(re.exec('The service is available')); // ? ["available", index: 15, input: "The service is available", groups: undefined] Unicode Property Escapes

ES2018 provides a new type of escape sequence known as Unicode property escape, which provides support for full Unicode in regular expressions. Suppose you want to match the Unicode character ? in a string. Although ? is considered a number, you can’t match it with the \d shorthand character class because it only supports ASCII [0-9] characters. Unicode property escapes, on the other hand, can be used to match any decimal number in Unicode:

const str = '?'; console.log(/\d/u.test(str)); // ? false console.log(/\p{Number}/u.test(str)); // ? true

Similarly, if you want to match any Unicode word alphabetic character, you can use \p{Alphabetic}:

const str = '?'; console.log(/\p{Alphabetic}/u.test(str)); // ? true // the \w shorthand cannot match ? console.log(/\w/u.test(str)); // ? false

There is also a negated version of \p{...}, which is denoted by \P{...}:

console.log(/\P{Number}/u.test('?')); // ? false console.log(/\P{Number}/u.test('?')); // ? true console.log(/\P{Alphabetic}/u.test('?')); // ? true console.log(/\P{Alphabetic}/u.test('?')); // ? false

In addition to Alphabetic and Number, there are several more properties that can be used in Unicode property escapes. You can find a list of supported Unicode properties in the current specification proposal.

Support for New RegExp Features Chrome Firefox Safari Edge s (dotAll) Flag 62 No 11.1 No Named Capture Groups 64 No 11.1 No Lookbehind Assertions 62 No No No Unicode Property Escapes 64 No 11.1 No Chrome (Android) Firefox (Android) iOS Safari Edge Mobile Samsung Internet Android Webview s (dotAll) Flag 62 No 11.3 No 8.2 62 Named Capture Groups 64 No 11.3 No No 64 Lookbehind Assertions 62 No No No 8.2 62 Unicode Property Escapes 64 No 11.3 No No 64

Node.js:

  • 8.3.0 (requires the --harmony runtime flag)
  • 8.10.0 (support for s (dotAll) flag and lookbehind assertions)
  • 10.0.0 (full support)
Template Literal Revision

When a template literal is immediately preceded by an expression, it is called a tagged template literal. A tagged template comes in handy when you want to parse a template literal with a function. Consider the following example:

function fn(string, substitute) { if(substitute === 'ES6') { substitute = 'ES2015' } return substitute + string[1]; } const version = 'ES6'; const result = fn`${version} was a major update`; console.log(result); // ? ES2015 was a major update

In this code, a tag expression — which is a regular function — is invoked and passed the template literal. The function simply modifies the dynamic part of the string and returns it.

Prior to ES2018, tagged template literals had syntactic restrictions related to escape sequences. A backslash followed by certain sequence of characters were treated as special characters: a \x interpreted as a hex escape, a \u interpreted as a unicode escape, and a \ followed by a digit interpreted as an octal escape. As a result, strings such as "C:\xxx\uuu" or "\ubuntu" were considered invalid escape sequences by the interpreter and would throw a SyntaxError.

ES2018 removes these restrictions from tagged templates and instead of throwing an error, represents invalid escape sequences as undefined:

function fn(string, substitute) { console.log(substitute); // ? escape sequences: console.log(string[1]); // ? undefined } const str = 'escape sequences:'; const result = fn`${str} \ubuntu C:\xxx\uuu`;

Keep in mind that using illegal escape sequences in a regular template literal still causes an error:

const result = `\ubuntu`; // ? SyntaxError: Invalid Unicode escape sequence Support for Template Literal Revision Chrome Firefox Safari Edge 62 56 11 No Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview 62 56 11 No 8.2 62

Node.js:

  • 8.3.0 (requires the --harmony runtime flag)
  • 8.10.0 (full support)
Wrapping up

We’ve taken a good look at several key features introduced in ES2018 including asynchronous iteration, rest/spread properties, Promise.prototype.finally(), and additions to the RegExp object. Although some of these features are not fully implemented by some browser vendors yet, they can still be used today thanks to JavaScript transpilers such as Babel.

ECMAScript is rapidly evolving and new features are being introduced every so often, so check out the list of finished proposals for the full scope of what’s new. Are there any new features you’re particularly excited about? Share them in the comments!

The post New ES2018 Features Every JavaScript Developer Should Know appeared first on CSS-Tricks.

Toggling Animations On and Off

Css Tricks - Wed, 01/09/2019 - 5:09am

A nicely detailed tutorial by Kirupa that gets into how you might provide a UI with persisted options that control whether animations run or not.

The trick is custom properties that control the movement:

body { --toggle: 0; --playState: "paused"; }

Which are used within animations and transitions:

.animation { animation: bobble 2s infinite; animation-play-state: var(--playState); } .transition { transition: transform calc(var(--toggle) * .15s) ease-in-out; }

And toggle-able by JavaScript:

// stop animation document.body.style.setProperty("--toggle", "0"); document.body.style.setProperty("--playState", "paused"); // play animation document.body.style.setProperty("--toggle", "1"); document.body.style.setProperty("--playState", "running");

Then get into using the media query to test for reduced motion off the bat, and storing the preferred value in localStorage.

Direct Link to ArticlePermalink

The post Toggling Animations On and Off appeared first on CSS-Tricks.

Styling a Web Component

Css Tricks - Tue, 01/08/2019 - 9:02am

This confused me for a bit here so I'm writing it out while it's fresh in mind. Just because you're using a web component doesn't mean the styles of it are entirely isolated. You might have content within a web component that is styled normally along with the rest of your website. Like this:

See the Pen Web Component with Global Styles (because no Shadow DOM) by Chris Coyier (@chriscoyier) on CodePen.

That <whats-up> element isolated the JavaScript-powered functionality of itself by attaching a click handler to the <button> inside of it. But the styling of that button comes from global CSS applied to that page.

Moving the template inside the web component

But let's say we move that <button> into the web component, so we can use <whats-up> all by itself. We could do that by .innerHTML'ing the custom element:

See the Pen Web Component with Global Styles (because no Shadow DOM) by Chris Coyier (@chriscoyier) on CodePen.

Again, entirely styled by the global CSS. Cool. That may be desirable. It also might not be desirable. Perhaps you're looking to web components to isolate styles for you.

Shadow DOMing the template

Web components can isolate styles (and abstract away HTML implementation) via the Shadow DOM. Here's that same component, using Shadow DOM instead:

See the Pen Web Component with Local Styles by Chris Coyier (@chriscoyier) on CodePen.

Note that the functionality still works (although we had to querySelector through the shadowRoot), but we've totally lost the global styling. The Shadow DOM boundary (shadow root) prevents styling coming in or going out (sorta like an iframe).

Shadow Root

There is no global way to penetrate that boundary that I'm aware of, so if you want to bring styles in, you gotta bring them into the template.

Move the styles (inline) inside the web component

See the Pen Web Component with Local Styles by Chris Coyier (@chriscoyier) on CodePen.

This would be highly obnoxious if you both really wanted to use the Shadow DOM but also wanted your global styles. It's funny that there is a Shadow DOM "mode" for open and closed for allowing or disallowing JavaScript in and out, but not CSS.

If that's you, you'll probably need to @import whatever global stylesheets you can to bring in those global styles and hope they are cached and the browser is smart about it in such a way that it isn't a big performance hit.

Link to external styles instead

I'll use CodePen's direct link to CSS feature to import the styles from the Pen itself into the web component:

See the Pen Web Component with Local Styles by Chris Coyier (@chriscoyier) on CodePen.

Apparently, there is no way to avoid somewhat of a Flash-Of-Unstyled-Component this way though, so inlining styles is recommended until there is.

Custom properties go through the shadow DOM

Another important thing to know is that CSS custom properties penetrate the Shadow DOM! That's right, they do. You can select the web component in the CSS and set them there:

See the Pen Web Component with Custom Properties by Chris Coyier (@chriscoyier) on CodePen.

HTML you point to via a slot is globally stylable

So if you have like:

<my-module> <h2 slot="header">My Module</h2> </my-module>

And where you define your shadow DOM you use that header:

<div class="module"> <slot name="header"></slot> </div>

Then the <h2> will be globally stylable, but that <div class="module"> will not.

See the Pen Slots and styling web components by Chris Coyier (@chriscoyier) on CodePen.

::part and ::theme

I didn't investigate this too much because this is a spec that's still being worked on I guess, but will likely ultimately play a large role here. Monica Dinculescu covers it in detail in her article ::part and ::theme, an ::explainer.

Looks like a way to reach into the shadow DOM, but only to the exact level that matches, no deeper.


#shadow-root

...

...

/* not styleable
x-foo::part(some-box) { ... } /* nope */ x-foo::part(some-box) span { ... }

The post Styling a Web Component appeared first on CSS-Tricks.

How To Learn CSS

Css Tricks - Tue, 01/08/2019 - 9:01am

Outside of my extreme envy of the SEO they are going to get out of this, Rachel is spot on here. Learning CSS has some pillars, like language syntax, selectors, layout, and flow that, once learned, unlock your CSS merit badge.

What I would add is that if you really want to learn CSS, give yourself a project that has real-world stakes (like a personal website), so what you are doing with CSS feels important and gives you the incentive to do interesting things and get it right.

Keith Grant has some interesting thoughts on relating the concept of a "Common Core" to CSS:

For example, if you need to add 32 + 67, break the problem up to 30 + 60 and 2 + 7, both of which are much easier to do in your head. As someone who excelled at math in school, I have found that most of these Common Core tricks are things I discovered on my own as a student, and are precisely why I was able to do well in the subject.

We need common core tricks like this for CSS.

Direct Link to ArticlePermalink

The post How To Learn CSS appeared first on CSS-Tricks.

Sass Techniques from the Trenches

Css Tricks - Tue, 01/08/2019 - 4:56am

Having been in the web development industry for more than 14 years, I’ve seen and written my fair share of good and bad CSS. When I began at Ramsey Solutions five years ago, I was introduced to Sass. It blew my mind how useful it was! I dove right in and wanted to learn everything I could about it. Over the past five years, I’ve utilized a number of different Sass techniques and patterns and fell in love with some that, to steal Apple’s phrase, just work.

In this article, I’ll explore a wide range of topics:

In my experience, finding the balance between simple and complex is the crucial component to making great software. Software should not only be easy for people to use, but for you and other developers to maintain in the future. I’d consider these techniques to be advanced, but not necessarily clever or complex, on purpose!

"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?"

—The Elements of Programming and Style (2nd Edition), Chapter 2

With that in mind, let’s first look at Sass’ ampersand.

The power of the ampersand

There are many different naming conventions you can use to organize your CSS. The one I enjoy using the most is SUIT, a variation of BEM (which is short for Block, Element, Modifier). If you’re unfamiliar with SUIT or BEM, I’d recommend taking a peek at one or both of them before moving on. I’ll be using the SUIT convention throughout the rest of this article.

Whatever naming convention you choose, the base idea is that every styled element gets its own class name, prepended with the component name. This idea is important for how some of the following organization works. Also, this article is descriptive, not prescriptive. Every project is different. You need to do what works best for your project and your team.

The ampersand is the main reason I like to use SUIT, BEM, and conventions like them. It allows me to use nesting and scoping without either biting back with specificity. Here’s an example. Without using the ampersand, I would need to create separate selectors to create -title and -content elements.

.MyComponent { .MyComponent-title {} } .MyComponent-content {} // Compiles to .MyComponent .MyComponent-title {} // Not what we want. Unnecessary specificity! .MyComponent-content {} // Desired result

When using SUIT, I want the second result for -content to be how I write all my selectors. To do so, I would need to repeat the name of the component throughout. This increases my chance to mistype the name of the component as I write new styles. It’s also very noisy as it ends up ignoring the beginning of many selectors which can lead to glossing over obvious errors.

.MyComponent {} .MyComponent-title {} .MyComponent-content {} .MyComponent-author {} // Etc.

If this were normal CSS, we’d be stuck writing the above. Since we’re using Sass, there’s a much better approach using the ampersand. The ampersand is amazing because it contains a reference to the current selector along with any parents.

.A { // & = '.A' .B { // & = '.A .B' .C { // & = '.A .B .C' } } }

You can see in the above example how the ampersand references each selector in the chain as it goes deeper into the nested code. By utilizing this feature, we can create new selectors without having to rewrite the name of the component each and every time.

.MyComponent { &-title {} &-content {} } // Compiles to .MyComponent {} .MyComponent-title {} .MyComponent-content {}

This is great because we can take advantage of the ampersand to write the name of the component one time and simply reference the component name throughout. This decreases the chance that the component name is mistyped. Plus, the document as a whole becomes easier to read without .MyComponent repeated all over the code.

There are times when the component needs a variant or modifier, as they’re called in SUIT and BEM. Using the ampersand pattern makes it easier to create modifiers.

<div class="MyComponent MyComponent--xmasTheme"></div> .MyComponent { &--xmasTheme {} } // Compiles to .MyComponent {} .MyComponent--xmasTheme {}

"But, what about modifying the child elements?" you might ask. "How are those selectors created? The modifier isn’t needed on every element, right?"

This is where variables can help!

Variables and scoping

In the past, I’ve created modifiers a few different ways. Most of the time, I’d rewrite the special theme name I want to apply when modifying the element.

.MyComponent { &-title { .MyComponent--xmasTheme & { } } &-content { .MyComponent--xmasTheme & { } } } // Compiles to .MyComponent-title {} .MyComponent--xmasTheme .MyComponent-title {} .MyComponent-content {} .MyComponent--xmasTheme .MyComponent-content {}

This gets the job done, but I’m back to rewriting the component name in multiple places, not to mention the modifier name. There’s definitely a better way to do this. Enter Sass variables.

Before we explore Sass variables with selectors, we need to understand how they’re scoped. Sass variables have scope, just like they would in JavaScript, Ruby, or any other programming language. If declared outside of a selector, the variable is available to every selector in the document after its declaration.

$fontSize: 1.4rem; .a { font-size: $fontSize; } .b { font-size: $fontSize; }

Variables declared inside a selector are scoped only to that selector and its children.

$fontSize: 1.4rem; .MyComponent { $fontWeight: 600; font-size: $fontSize; &-title { font-weight: $fontWeight; // Works! } } .MyComponent2 { font-size: $fontSize; &-title { font-weight: $fontWeight; // produces an "undefined variable" error } }

We know variables can store font names, integers, colors, etc. Did you know it can also store selectors? Using string interpolation, we can create new selectors with the variable.

// Sass string interpolation syntax is #{VARIABLE} $block: ".MyComponent"; #{$block} { &-title { #{$block}--xmasTheme & { } } } // Compiles to .MyComponent {} .MyComponent-title {} .MyComponent--xmasTheme .MyComponent-title {}

That’s cool, but the variable is globally scoped. We can fix that by creating the $block variable inside the component declaration, which would scope it to that component. Then we can re-use the $block variable in other components. This helps DRY up the theme modifier.

.MyComponent { $block: '.MyComponent'; &-title { #{$block}--xmasTheme & { } } &-content { #{$block}--xmasTheme & { } } } // Compiles to .MyComponent {} .MyComponent-title {} .MyComponent--xmasTheme .MyComponent-title {} .MyComponent-content {} .MyComponent--xmasTheme .MyComponent-content {}

This is closer, but again, we have to write the theme name over and over. Let’s store that in a variable too!

.MyComponent { $block: '.MyComponent'; $xmasTheme: '.MyComponent--xmasTheme'; &-title { #{$xmasTheme} & { } } }

This is much better! However, we can improve this even further. Variables can also store the value of the ampersand!

.MyComponent { $block: &; $xmasTheme: #{&}--xmasTheme; &-title { #{$xmasTheme} & { } } } // Still compiles to .MyComponent {} .MyComponent-title {} .MyComponent--xmasTheme .MyComponent-title {}

Now that’s what I’m talking about! "Caching" the selector with ampersand allows us to create our modifiers at the top and keep the theme modifications with the element it’s modifying.

"Sure, that works at the top level," you say. "But what if you are nested really deep, like eight levels in?" You ask great questions.

No matter how deep the nest, this pattern always works because the main component name is never attached to any of the children, thanks to the SUIT naming convention and ampersand combo.

.MyComponent { $block: &; $xmasTheme: #{&}--xmasTheme; &-content { font-size: 1.5rem; color: blue; ul { li { strong { span { &::before { background-color: blue; #{$xmasTheme} & { background-color: red; } } } } } } } } // Compiles to .MyComponent-content { font-size: 1.5rem; color: blue; } .MyComponent-content ul li strong span::before { background-color: blue; } /* * The theme is still appended to the beginning of the selector! * Now, we never need to write deeply nested Sass that's hard to maintain and * extremely brittle: https://css-tricks.com/sass-selector-combining/ */ .MyComponent--xmasTheme .MyComponent-content ul li strong span::before { background-color: red; }

Code organization is the main reason I like to use this pattern.

  • It’s relatively DRY
  • It supports the "opt-in" approach, which keeps modifiers with the elements they modify
  • Naming stuff is hard but this enables us to reuse common element names like "title" and "content"
  • It’s low-lift to add a modifier to a component by placing the modifier class on the parent component

"Hhhmmmmm... doesn’t that get hard to read though after you create a bunch of different components? How do you know where you’re at when everything is named &-title and &-content?"

You continue to ask great questions. Who said the source Sass had to be in one file? We can import those components, so let’s turn to that topic!

The importance of imports Credit: @Julien_He

One of Sass’ best features is @import. We can create separate Sass files (partials) and import them into other Sass files that compile together with the imported file located at the spot it’s imported. This makes it easy to package up related styles for components, utilities, etc. and pull them into a single file. Without @import, we’d need to link to separate CSS files (creating numerous network requests, which is badong) or write everything in a single stylesheet (which is tough to navigate and maintain).

.Component1 { &-title {} &-content {} &-author {} } .Component2 { &-title {} &-content {} &-author {} } .Component3 { &-title {} &-content {} &-author {} } .Component4 { &-title {} &-content {} &-author {} } .Component5 { &-title {} &-content {} &-author {} } // A couple hundred lines later... .Component7384 { &-title {} &-content {} &-author {} } // WHERE AM I?

One of the more popular methodologies for organizing Sass files is the 7-1 Pattern. That’s seven distinct folders containing Sass files that are imported into a single Sass file.

Those folders are:

  • abstracts
  • base
  • components
  • layout
  • pages
  • themes
  • vendor

Use @import to pull each Sass file in those folder into a main Sass file. We want to import them in the following order to maintain good scope and avoid conflicts during compilation:

  1. abstracts
  2. vendor
  3. base
  4. layout
  5. components
  6. pages
  7. themes
@import 'abstracts/variables'; @import 'abstracts/functions'; @import 'abstracts/mixins'; @import 'vendors/some-third-party-component'; @import 'base/normalize'; @import 'layout/navigation'; @import 'layout/header'; @import 'layout/footer'; @import 'layout/sidebar'; @import 'layout/forms'; @import 'components/buttons'; @import 'components/hero'; @import 'components/pull-quote'; @import 'pages/home'; @import 'pages/contact'; @import 'themes/default'; @import 'themes/admin';

You may or may not want to use all of these folders (I personally don’t use the theme folder since I keep themes with their components), but the idea of separating all of styles into distinct files makes it easier to maintain and find code.

More of the benefits of using this approach:

  • Small components are easier to read and understand
  • Debugging becomes simpler
  • It’s clearer to determine when a new component should be created — like when a single component file gets to be too long, or the selector chain is too complex
  • This emphasizes re-usage — for example, it might make sense to generalize three component files that essentially do the same thing into one component

Speaking of re-usage, there are eventually patterns that get used often. That’s when we can reach for mixins.

Mixin’ it up

Mixins are a great way to reuse styles throughout a project. Let’s walk through creating a simple mixin and then give it a little bit of intelligence.

The designer I work with on a regular basis always sets font-size, font-weight, and line-height to specific values. I found myself typing all three out every time I needed to adjust the fonts for a component or element, so I created a mixin to quickly set those values. It’s like a little function I can use to define those properties without having to write them in full.

@mixin text($size, $lineHeight, $weight) { font-size: $size; line-height: $lineHeight; font-weight: $weight; }

At this point, the mixin is pretty simple—it resembles something like a function in JavaScript. There’s the name of the mixin (text) and it takes in three arguments. Each argument is tied to a CSS property. When the mixin is called, Sass will copy the properties and the pass in the argument values.

.MyComponent { @include text(18px, 27px, 500); } // Compiles to .MyComponent { font-size: 18px; line-height: 27px; font-weight: 500; }

While it’s a good demonstration, this particular mixin is a little limited. It assumes we always want to use the font-size, line-height, and font-weight properties when it’s called. So let’s use Sass’ if statement to help control the output.

@mixin text($size, $lineHeight, $weight) { // If the $size argument is not empty, then output the argument @if $size != null { font-size: $size; } // If the $lineHeight argument is not empty, then output the argument @if $lineHeight != null { line-height: $lineHeight; } // If the $weight argument is not empty, then output the argument @if $weight != null { font-weight: $weight; } } .MyComponent { @include text(12px, null, 300); } // Compiles to .MyComponent { font-size: 12px; font-weight: 300; }

That’s better, but not quite there. If I try to use the mixin without using null as a parameter on the values I don’t want to use or provide, Sass will generate an error:

.MyComponent { @include text(12px, null); // left off $weight } // Compiles to an error: // "Mixin text is missing argument $weight."

To get around this, we can add default values to the parameters, allowing us to leave them off the function call. All optional parameters have to be declared after any required parameters.

// We define `null` as the default value for each argument @mixin text($size: null, $lineHeight: null, $weight: null) { @if $size != null { font-size: $size; } @if $lineHeight != null { line-height: $lineHeight; } @if $weight != null { font-weight: $weight; } } .MyComponent { &-title { @include text(16px, 19px, 600); } &-author { @include text($weight: 800, $size: 12px); } } // Compiles to .MyComponent-title { font-size: 16px; line-height: 19px; font-weight: 600; } .MyComponent-author { font-size: 12px; font-weight: 800; }

Not only do default argument values make the mixin easier to use, but we also gain the ability to name parameters and give them values that may be commonly used. On Line 21 above, the mixin is being called with the arguments out of order, but since the values are being called out as well, the mixin knows how to apply them.

There’s a particular mixin that I use on a daily basis: min-width. I prefer to create all my sites mobile first, or basically with the smallest viewport in mind. As the viewport grows wider, I define breakpoints to adjust the layout and the code for it. This is where I reach for the min-width mixin.

// Let's name this "min-width" and take a single argument we can // use to define the viewport width in a media query. @mixin min-width($threshold) { // We're calling another function (scut-rem) to convert pixels to rem units. // We'll cover that in the next section. @media screen and (min-width: scut-rem($threshold)) { @content; } } .MyComponent { display: block; // Call the min-width mixin and pass 768 as the argument. // min-width passes 768 and scut-rem converts the unit. @include min-width(768) { display: flex; } } // Compiles to .MyComponent { display: block; } @media screen and (min-width: 48rem) { .MyComponent { display: flex; } }

There are a couple of new ideas here. The mixin has a nested function called @content. So, in the .MyComponent class, we’re no longer calling the mixin alone, but also a block of code that gets output inside the media query that’s generated. The resulting code will compile where @content is called. This allows the mixin to take care of the @media declaration and still accept custom code for that particular breakpoint.

I also am including the mixin within the .MyComponent declaration. Some people advocate keeping all responsive calls in a separate stylesheet to reduce the amount of times @media is written out in a stylesheet. Personally, I prefer to keep all variations and changes that a component can go through with that component’s declaration. It tends to make it easier to keep track of what’s going on and help debug the component if something doesn’t go right, rather than sifting through multiple files.

Did you notice the scut-rem function in there? That is a Sass function taken from a Sass library called Scut, created by David The Clark. Let’s take a look at how that works.

Getting functional

A function differs from a mixin in that mixins are meant to output common groups of properties, while a function modifies properties based on arguments that return a new result. In this case, scut-rem takes a pixel value and converts it to a rem value. This allows us to think in pixels, while working with rem units behind the scenes to avoid all that math.

I’ve simplified scut-rem in this example because it has a few extra features that utilize loops and lists, which are out of the scope of what we’re covering here. Let’s look at the function in its entirety, then break it down step-by-step.

// Simplified from the original source $scut-rem-base: 16 !default; @function scut-strip-unit ($num) { @return $num / ($num * 0 + 1); } @function scut-rem ($pixels) { @return scut-strip-unit($pixels) / $scut-rem-base * 1rem; } .MyComponent { font-size: scut-rem(18px); } // Compiles to .MyComponent { font-size: 1.125rem; }

The first thing to note is the declaration on Line 2. It’s using !default when declaring a variable, which tells Sass to set the value to 16 unless this variable is already defined. So if a variable is declared earlier in the stylesheet with a different value, it won’t be overridden here.

$fontSize: 16px; $fontSize: 12px !default; .MyComponent { font-size: $fontSize; } // Compiles to .MyComponent { font-size: 16px; }

The next piece of the puzzle is scut-strip-unit. This function takes a px, rem, percent or other suffixed value and removes the unit label. Calling scut-strip-unit(12px) returns 12 instead of 12px. How does that work? In Sass, a unit divided by another unit of the same type will strip the unit and return the digit.

12px / 1px = 12

Now that we know that, let’s look at the scut-strip-unit function again.

@function scut-strip-unit ($num) { @return $num / ($num * 0 + 1); }

The function takes in a unit and divides it by 1 of the same unit. So if we pass in 12px, the function would look like: @return 12px / (12px * 0 + 1). Following the order of operations, Sass evaluates what’s in the parentheses first. Sass smartly ignores the px label, evaluates the expression, and tacks px back on once it’s done: 12 * 0 + 1 = 1px. The equation is now 12px / 1px which we know returns 12.

Why is this important to scut-rem? Looks look at it again.

$scut-rem-base: 16 !default; @function scut-rem ($pixels) { @return scut-strip-unit($pixels) / $scut-rem-base * 1rem; } .MyComponent { font-size: scut-rem(18px); }

On Line 4, the scut-strip-unit function removes px from the argument and returns 18. The base variable is equal to 16 which turns the equation into: 18 / 16 * 1rem. Remember, Sass ignores any unit until the end of the equation, so 18 / 16 = 1.125. That result multiplied by 1rem gives us 1.125rem. Since Scut strips the unit off of the argument, we can call scut-rem with unit-less values, like scut-rem(18).

I don’t write that many functions because I try to keep the stuff I create as simple as possible. Being able to do some complex conversions using something like scut-rem is helpful though.

The selector order that placeholders mess up End up where I think it did, that CSS?

I really don’t like to use placeholders and @extend in my code. I find it easy to get in trouble with them for a couple different reasons.

Be careful what is extended

I tried writing out some examples to demonstrate why using @extend can be problematic, but I have used them so little that I can’t create any decent examples. When I first learned Sass, I was surrounded by teammates who’ve already gone through the trials and tribulations. My friend Jon Bebee wrote an extremely excellent article on how @extend can get you into trouble. It’s a quick read and worth the time, so I’ll wait.

About those placeholders...

Jon proposes using placeholders as a solution to the problem he outlines: Placeholders don’t output any code until they’re used with @extend.

// % denotes an extended block %item { display: block; width: 50%; margin: 0 auto; } .MyComponent { @extend %item; color: blue; } // Compiles to .MyComponent { display: block; width: 50%; margin: 0 auto; } .MyComponent { color: blue; }

OK, wait. So it output .MyComponent twice? Why didn’t it simply combine the selectors?

These are the questions I had when I first started using placeholders (and then subsequently stopped). The clue is the name itself. Placeholders simply hold a reference to the place in the stylesheet they were declared. While a mixin copies the properties to the location it is used, placeholders copy the selector to the place where the placeholder was defined. As a result, it copies the .MyComponent selector and places it where %item is declared. Consider the following example:

%flexy { display: flex; } .A { color: blue; } .B { @extend: %flexy; color: green; } .C { @extend: %flexy; color: red; } // Compiles to .B, .C { display: flex; } .A { color: blue; } .B { color: green; } .C { color: red; }

Even though B and C are declared further down in the stylesheet, the placeholder places the extended properties tall the way up to where it was originally declared. That’s not a big deal in this example because it’s really close to the source where it’s used. However, if we’re adhering to something like the 7-1 Pattern we covered earlier, then placeholders would be defined in a partial in the abstracts folder, which is one of the first imported files. That puts a lot of style between where the extend is intended and where it’s actually used. That can be hard to maintain as well as hard to debug.

Sass Guidelines (of course) does a great job covering placeholders and extend and I would recommend reading it. It not only explains the extend feature, but at the end, advocates against using it:

Opinions seem to be extremely divided regarding the benefits and problems from @extend to the point where many developers including myself have been advocating against it, [...]

There are many other features of Sass I didn’t cover here, like loops and lists, but I’ve honestly haven’t relied on those features as much as the ones we did cover in this article. Take a look through the Sass documentation, if for nothing else, to see what things do. You may not find a use for everything right away, but a situation may come up and having that knowledge in your back pocket is priceless.

Let me know if I missed something or got something wrong! I’m always open to new ideas and would love to discuss it with you!

Further Reading

The post Sass Techniques from the Trenches appeared first on CSS-Tricks.

The 10,000 Year Clock Design Principals

Css Tricks - Tue, 01/08/2019 - 4:55am

In the new year edition of the Clearleft newsletter, Jeremy Keith linked to the design principals Danny Hillis thought about while considering a clock that would work for 10,000 years.

Here's part of that page, satisfyingly displayed as a <dl>:

Longevity:
Go slow
Avoid sliding friction (gears)
Avoid ticking
Stay clean
Stay dry
Expect bad weather
Expect earthquakes
Expect non-malicious human interaction
Dont tempt thieves
Maintainability and transparency:
Use familiar materials
Allow inspection
Rehearse motions
Make it easy to build spare parts
Expect restarts
Include the manual
Scalability and Evolvabilty:
Make all parts similar size
Separate functions
Provide simple interfaces

How... good.

Direct Link to ArticlePermalink

The post The 10,000 Year Clock Design Principals appeared first on CSS-Tricks.

Reader Mode: The Button to Beat

Css Tricks - Mon, 01/07/2019 - 4:57am

As a young nerd, I loved to immerse myself in digital worlds, learning the ins and outs of the rules someone else had created for me (intentionally or not). But the older and crankier I get, the more I find myself losing patience when navigating these "delightful" experiences.

This fascination was great for my eventual career as a designer, but unfortunately, it was also like teaching someone kerning—once you learn how to quantify a bad user experience, you can’t go back.

These days, I’m an impatient grump who doesn't want to take work home. I just want to get in, get what I need, and get out. If there’s any delight I’m experiencing, it’s lost on me because I had such an effortless and annoyance-free time that it simply doesn’t stand out.

One of the features I find myself turning to over and over again is Safari’s Reader Mode. I read a lot of news, and with that comes a lot of bullshit. I now tap the cryptic little icon almost reflexively, confident that I’ll be transported to a land where I can focus on what matters most to me: content.

Tapping this button transports me to a land free of newsletter signup modals, surveys, pop-ups, pop-unders, flashing ad banners, automatically-playing video, app install prompts, breaking news alerts, passive-aggressive interstitials, and faux notification permission banners. It slices through the undesirable and unnecessary with ease; the Alexander the Great to the Gordian Knot that is poor user interface design.

Firefox also offers this reading mode. So does Edge. I find myself using it more and more on my laptop with every passing day—especially for reading long-form articles, like this piece. I’d be very surprised to see Chrome institute one natively, as Google is ultimately in the advertising business.

I’m not going to talk about how to best craft your content for Reader Mode. Mandy Michael already covered this in her article, Building websites for Safari Reader Mode and other reading apps. She’s great, and it is a must-read piece.

Building with accessible HTML standards is not a dead-end skill. Far from it. If you spend the effort to craft your experiences with a mind to semantics from the start, your content will be able to adapt to specialized reading modes, as well as whatever the future holds with little to no additional effort. Today’s Reader Mode could be tomorrow’s smart bathroom mirror.

Spending the effort is an important point: Good design isn’t about forcing someone to walk a tightrope across your carefully manicured lawn. Nor is it a puzzle box casually tossed to the user, hoping they’ll unlock it to reveal a hidden treasure. Good design is about doing the hard work to accommodate the different ways people access a solution to an identified problem.

For reading articles, the core problem is turning my ignorance about an issue into understanding (the funding model for this is a whole other complicated concern). The more obstructions you throw in my way to achieve this goal, the more I am inclined to leave and get my understanding elsewhere—all I’ll remember is how poor a time I had while trying to access your content. What is the value of an ad impression if it ultimately leads to that user never returning?

But this isn’t a website about digital media strategy, nor is it one about user conversion. This is a website about CSS and front-end development. What we’re going to discuss is how to keep people like me from hitting that button by relying on this nifty programming language the W3C so wisely gave us. Because if you don’t, all that other stuff—your newsletter signup boxes, your comments, your related articles, your engagement—will be cut away.

Inclusivity

What you want to do first is cast a wide net. The more people you can proactively accommodate from the outset, the more people you don’t unintentionally alienate. Our design choices should be invisible—we’re not trying to say, "this is for you." That should be self-evident. What we’re trying to avoid are scenarios where someone encounters something that communicates, "this is for someone else."

It’s not too difficult, provided you know what to look out for. Carie Fisher outlines the bulk of it in her brilliant post, Designing Accessible Content: Typography, Font Styling, and Structure.

Priority

A basic paragraph style is the wellspring from which all your other type decisions should flow. It’s probably the most common and frequently invoked content type a website has, so it’s important to treat it with the care and respect it deserves. The web is typography, after all.

Heydon Pickering wrote about styling paragraphs way back when in 2011 with his post, The Perfect Paragraph. And here’s the thing: eight years later, this is all still solid advice (sheesh, I’ve been doing this for awhile). When you make design decisions that work with the grain of the web platform, you gain the confidence that you’re creating resilient, robust, and accessible solutions that last.

The neat part about this is that it frees up time to do other things, say reading about gender bias and the undervaluing of HTML and CSS. If anything, do it for me. I am honestly not sure I can handle another case of 2,000 lines of JavaScript used to recreate position: absolute;.

Circumstance Form

Even though responsive design is nearly a decade old at this point(!), we still seem to ignore a lot of the wisdom Ethan Marcotte so nicely teaches us for free. He’s a smart guy, you should pay attention to what he has to say.

After a complete lack of breakpoints, perhaps the biggest offender I still come across with regards to responsive design is the assumption that a small viewport means teeny-tiny type. Typically, the opposite is true. Small devices are made to be worn or carried, meaning that we move them in physical space to get them into a comfortable reading position. This is the opposite of a larger, more stationary device, such as a monitor, where we move our body to accommodate it instead.

A comfortable reading position means not forcing someone to hold a phone two centimeters away from their face. Ergonomics aren’t likely to change, but devices will. Because of that, you should craft your breakpoint names to be abstract. I personally like names that keep usability in mind, so something along the lines of, "wrist, palm, lap, desk, wall." It helps keep the user’s circumstance top-of-mind, and moves you away from associating only certain kinds of content as being viable on certain kinds of devices.

These ergonomically-derived designs can be achieved with the help from people like Rachel Andrew, whose in-depth explorations of CSS grid help us understand the power behind a real CSS layout system. Sass experts like Miriam Suzanne then teach us how to use True to codify these layouts and reliably integrate them into our larger Sass systems.

You also want to avoid fallacious device sniffing approaches, or making gross assumptions about a user’s circumstances and capabilities. Just let me increase and decrease that type size. Reader Mode lets me, so I’m going to get there one way or another.

Connection

The other thing you need to think about is how that ideal paragraph design actually gets served to a device. A big part of that involves loading our fonts, and ensuring that the loading process prioritizes user experience.

Text

Text downloads quickly; a lot faster than other exotic kinds of content. Browsers will render it gleefully, as it is historically the most important part of the payload. This means that the Reader Mode button is going to show up a lot faster than that distracting auto-playing video of talking heads so thoughtfully jammed into the bottom right-hand corner of my viewport.

And what if we’re on a slow, intermittent, and/or metered connection? Top-of-the-line MacBooks still have to use hotel wifi, just like everyone else.

You want to keep the page from jumping around when our paragraph font loads. This prevents the terrible experience of forcing me to scroll around to rediscover my place as things shift into place. It also helps prevent me from mis-clicking, taking me away from what I want to read because I had the audacity to interact with the page before the bitcoin miners are deployed (thankfully, good people like Laura Kalbag can help us with that one).

The temptation to hit that Reader Mode button is strong, because when I see the main text of the page show up, I know I can easily and reliably avoid all these potential issues.

Helen V. Holmes wrote Type is Your Right!, a beautiful article that effortlessly blends typographic history, capability, and performance. Notably, she discusses how to manage the Flash of Invisible Text (FOIT) and Flash of Unstyled Text (FOUT) to best corral all the aforementioned issues. In response, Monica Dinculescu made Font style matcher, a fantastic tool that lets you bend, stretch, squish, squash, and torture type in ways that would make your stodgy typography professor faint, all in the service of preventing layout jank.

Images

You can (and should) make all sorts of clever optimizations to ensure we’re delivering our images as efficiently as possible. But what happens while I’m waiting for those images to show up? What if they never do?

Since you’re a responsible, inclusive web professional, you’ve already made sure to include alternative text descriptions for our image content. Ire Aderinokun teaches us that you can take that one step further and style broken images. Now even the content that isn’t working as intended looks good. No brittle, overwrought JavaScript here—just good, old fashioned progressive enhancement.

The other type of image you want to consider are icons. There’s lots of reasons to not use icon fonts. Adding one more reason to toss on the pile: icon fonts may not hold up in Reader Mode, as they are constructed using text glyphs. When Reader Mode passes over a page, it may convert the glyph to use the font you specify. This could make for a disastrous experience, especially if the icon is used to communicate critical functionality (e.g. "Press the Home button (☒) to return to the main menu.").

To avoid this issue, Sara Soueidan teaches us how to convert those icon fonts to SVG . But you know what? She’s so much more than just a SVG expert. She’s an incredible UX developer, and you’d do well to read up on what she’s written. I, for one, have learned a ton.

Control

To help make my reading experience as comfortable as possible, Reader Mode allows me to adjust things like the typeface, the text and background colors, the font size and line height, and the number of words per line. This is great. I’ll frequently toggle back and forth between light and dark backgrounds depending on the time of day.

I also wear glasses, and I know that the older I get, the worst my vision will be. Thanks to Jennifer Aldrich’s writing, I know that this is the norm. After all, we’re all just temporarily abled. I might also need something like Windows High Contrast Mode one day. Thanks to Amelia Bellamy-Royds, I now know how to make my content be the best it can be when viewed in that mode.

The web is flexible. Working on it means getting over your ego and learning to let go. That means accepting that the medium will never be pixel perfect. It means embracing technology like relative units, and more importantly, philosophies like Intrinsic Web Design. That’s brought to us by Jen Simmons, a tireless and passionate advocate for web standards.

I’d love to read your website. I’d love for your harmonious typography to quietly usher me into a flow state, making me forget I was even browsing your site at all.

The post Reader Mode: The Button to Beat appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.