How Well Do You Know CSS Layout?

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: content-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

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

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

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

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! ⤵️

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

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

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

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.

Screenshot. Shows a diagonal gradient disc that has been split into eight horizontal slices, one on top of the other, with tiny gaps in between them and slightly offset to the left or right (with respect to the vertical axis of the disc) based on parity.
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

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

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

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.