Archiwum kategorii: CSS

New in Chrome 63

Post pobrano z: New in Chrome 63

Yeah, we see browser updates all the time these days and you may have already caught this one. Aside from slick new JavaScript features, there is one new CSS update in Chrome 63 that is easy to overlook but worth calling out:

Chrome 63 now supports the CSS overscroll-behavior property, making it easy to override the browser’s default overflow scroll behavior.

The property is interesting because it natively supports the pull to refresh UI that we often see in native and web apps, defines scrolling regions that are handy for popovers and slide-out menus, and provides a method to control the rubber-banding effect on some touch devices so that a page does a hard stop at the top and bottom of the viewport.

For now, overscroll-behavior is not a W3C standard (here’s the WICG proposed draft). It’s currently only supported by Chrome (63, of course) which also means it’s in Opera (version 50). Chrome Platform Status reports that it is currently in development for Firefox and has public support from Edge.

Direct Link to Article — Permalink


New in Chrome 63 is a post from CSS-Tricks

Using SVG to Create a Duotone Effect on Images

Post pobrano z: Using SVG to Create a Duotone Effect on Images

Anything is possible with SVG, right?!

After a year of collaborating with some great designers and experimenting to achieve some pretty cool visual effects, it is beginning to feel like it is. A quick search of „SVG” on CodePen will attest to this. From lettering, shapes, sprites, animations, and image manipulation, everything is better with the aid of SVG. So when a new visual trend hit the web last year, it was no surprise that SVG came to the rescue to allow us to implement it.

The spark of a trend

Creatives everywhere welcomed the 2016 new year with the spark of a colorizing technique popularized by Spotify’s 2015 Year in Music website (here is last year’s) which introduced bold, duotone images to their brand identity.

The Spotify 2015 Year in Music site demonstrates the duotone image technique.

This technique is a halftone reproduction of an image by superimposing one color (traditionally black) with another. In other words, the darker tone will be mapped to the shadows of the image, and the lighter tone, mapped to the highlights.

We can achieve the duotone technique in Photoshop by applying a gradient map (Layer > New Adjustment Layer > Gradient Map) of two colors over an image.

Choose the desired color combination for the gradient map
A comparison of the original image (left) and when the gradient map is applied (right)

Right click (or alt + click) the adjustment layer and create a clipping mask to apply the gradient map to just the image layer directly below it instead of the applying to all layers.

It used to take finessing the <canvas> element to calculate the color mapping and paint the result to the DOM or utilize CSS blend-modes to come close to the desired color effect. Well, thanks to the potentially life-saving powers of SVG, we can create these Photoshop-like “adjustment layers” with SVG filters.

Let’s get SaVinG!

Breaking down the SVG

We are already familiar with the vectorful greatness of SVG. In addition to producing sharp, flexible, and small graphics, SVGs also support over 20 filter effects that allow us to blur, morph, and do so much more to our SVG files. For this duotone effect, we will use two filters to construct our gradient map.

feColorMatrix (optional)

The feColorMatrix effect allows us to manipulate the colors of an image based on a matrix of rbga channels. Una Kravets details color manipulation with feColorMatrix in this deep dive and it’s a highly recommended read.

Depending on your image, it may be worth balancing the colors in the image by setting it to grayscale with the color matrix. You can adjust the rbga channels as you’d like for the desired grayscale effect.

<feColorMatrix type="matrix" result="grayscale"
  values="1 0 0 0 0
    1 0 0 0 0
    1 0 0 0 0
    0 0 0 1 0" >
</feColorMatrix>

feComponentTransfer

Next is to map the two colors over the highlights and shadows of our grayscale image with the feComponentTransfer filter effect. There are specific element attributes to keep in mind for this filter.

Attribute What it Does Value to Use
color-interpolation-filters (required) Specifies the color space for gradient interpolations, color animations, and alpha compositing. sRGB
result (optional) Assigns a name to this filter effect and can be used/referenced by another filter primitive with the in attribute. duotone

While the result attribute is optional, I like to include it to give additional context to each filter (and as a handy note for future reference).

The feComponent filter handles the color mapping based on transfer functions of each rbga component specified as child elements of the parent feComponentTransfer: feFuncR feFuncG feFuncB feFuncA. We use these rbga functions to calculate the values of the two colors in the gradient map.

Here’s an example:

The Peachy Pink gradient map in the screenshots above uses a magenta color (#bd0b91) , with values of R(189) G(11) B(145).

Divide each RGB value by 255 to get the values of the first color in the matrix. The RGB values of the second column result in #fcbb0d (gold). Similar to in our Photoshop gradient map, the first color (left to right) gets mapped to the shadows, and the second to the highlights.

<feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
  <feFuncR type="table" tableValues="(189/255) 0.9882352941"></feFuncR>
  <feFuncG type="table" tableValues="(11/255) 0.7333333333"></feFuncG>
  <feFuncB type="table" tableValues="(145/255) 0.05098039216"></feFuncB>
  <feFuncA type="table" tableValues="0 1"></feFuncA>
</feComponentTransfer>

Step 3: Apply the Effect with a CSS Filter

With the SVG filter complete, we can now apply it to an image by using the CSS filter property and setting the url() filter function to the ID of the SVG filter.

It’s worth noting that the SVG containing the filter can just be a hidden element sitting right in your HTML. That way it loads and is availble for use, but does not render on the screen.

background-image: url('path/to/img');
filter: url(/path/to/svg/duotone-filters.svg#duotone_peachypink);
filter: url(#duotone_peachypink);

Browser Support

You’re probably interested in how well supported this technique is, right? Well, SVG filters have good browser support.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
8 9 3 10 12 6

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
6.0-6.1 10 all 4.4 62 57

That said, CSS filters are not as widely supported. That means some graceful degradation considerations will be needed.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
18* 15* 35 No 17 6*

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
6.0-6.1* 37* No 4.4* 62 57

For example, Internet Explorer (IE) does not support the CSS Filter url() function, nor does it support CSS background-blend-modes, the next best route to achieving the duotone effect. As a result, a fallback for IE can be an absolutely positioned CSS gradient overlay on the image to mimic the filter.

In addition, I did have issues in Firefox when accessing the filter itself based on the path for the SVG filter when I initially implemented this approach on a project. Firefox seemed to work only if the filter was referenced with the full path to the SVG file instead of the filter ID alone. This does not seem to be the case anymore but is worth keeping in mind.

Bringing it All Together

Here’s a full example of the filter in use:

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="duotone_peachypink">
    <feColorMatrix type="matrix" result="grayscale"
      values="1 0 0 0 0
              1 0 0 0 0
              1 0 0 0 0
              0 0 0 1 0" >
    </feColorMatrix>
    <feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
      <feFuncR type="table" tableValues="0.7411764706 0.9882352941"></feFuncR>
      <feFuncG type="table" tableValues="0.0431372549 0.7333333333"></feFuncG>
      <feFuncB type="table" tableValues="0.568627451 0.05098039216"></feFuncB>
      <feFuncA type="table" tableValues="0 1"></feFuncA>
    </feComponentTransfer> 
  </filter>        
</svg>

Here’s the impact that has when applied to an image:

A comparison of the original image (left) with the filtered effect (right) using SVG!

See the Pen Duotone Demo by Lentie Ward (@lentilz) on CodePen.

For more examples, you can play around with more duotone filters in this pen.

Resources

The following resources are great points of reference for the techniques used in this post.


Using SVG to Create a Duotone Effect on Images is a post from CSS-Tricks

How Would You Solve This Rendering Puzzle In React?

Post pobrano z: How Would You Solve This Rendering Puzzle In React?

Welcome, React aficionados and amateurs like myself! I have a puzzle for you today.

Let’s say that you wanted to render out a list of items in a 2 column structure. Each of these items is a separate component. For example, say we had a list of albums and we wanted to render them a full page 2 column list. Each „Album” is a React component.

Scroll rendering problem

Now assume the CSS framework that you are using requires you to render out a two column layout like this…

<div class="columns">
  <div class="column"> Column 1 </div>
  <div class="column"> Column 2 </div>
<div class="columns">

This means that in order to render out the albums correctly, you have to open a columns div tag, render two albums, then close the tag. You do this over and over until all the albums have been rendered out.

I solved it by breaking the set into chunks and rendering on every other album conditionally in a separate render function. That render function is only called for every other item.

class App extends Component { 
  state = {albums: [] }

  async componentDidMount() { 
    let data = Array.from(await GetAlbums());
    this.setState({ albums: data } ); 
  } 

  render() { 
    return (
      <section className="section">
        {this.state.albums.map((album, index) => {
          // use the modulus operator to determine even items return index % 2 ?
          this.renderAlbums(index) : '';
        })}
      </section> 
    )
  }

  renderAlbums(index) { 
    // two albums at a time - the current and previous item
    let albums = [this.state.albums[index - 1], this.state.albums[index]]; 
    
    return (
      <div className="columns" key={index}>
        {albums.map(album => {
          return (
            <Album album={album} />
          );
        })}
      </div>
    );
  }
}

View Full Project

Another way to do this would be to break the albums array up into a two-dimensional array and iterate over that. The first highlighted block below splits up the array. The second is the vastly simplified rendering logic.

class App extends Component { 
  state = {albums: []}
  
  async componentDidMount() { 
    let data = Array.from(await GetAlbums());
    
    // split the original array into a collection of two item sets
    data.forEach((item, index) => {
      if (index % 2) {
        albums.push([data[index - 1], data[index]]);
      }
    });

    this.setState({
      albums: albums
    });
  } 

  render() {
    return ( 
      <section className="section">
        {this.state.albums.map((album, index) => { 
          return ( 
            <div className="columns">
              <Album album={album[0]}></Album> 
              <Album album={album[1]}></Album> 
            </div> 
          ) 
        })}
      </section> 
    ) 
  } 
}

View Full Project

This cleans up the JSX quite a bit, but now I’m redundantly entering the Album component, which just feels wrong.

Sarah Drasner pointed out to me that I hadn’t even considered one of the more important scenarios here, and that is the unknown bottom scenario.

Unknown Bottom

Both of my solutions above assume that the results set received from the fetch is final. But what if it isn’t?

What if we are streaming data from a server (ala RxJs style) and we don’t know how many times we will receive a results set, and we don’t know how many items will be in a given set. That seriously complicates things and utterly destroys the proposed solutions. In fact, we could go ahead and say that neither of these solutions are ideal because they don’t scale to this use case.

I feel like the absolute simplest solution here would be to fix this in the CSS. Let the CSS worry about the layout the way God intended. I still think it’s important to look at how to do this with JSX because there are people building apps in the real world who have to deal with shenanigans like this every day. The requirements are not always what we want them to be.

How Would You Do It?

My question is just that — how would you do this? Is there a cleaner more efficient way? How can this be done so that it scales with an unknown bottom? Inquiring minds (mine specifically) would love to know.


How Would You Solve This Rendering Puzzle In React? is a post from CSS-Tricks

Evolution of img: Gif without the GIF

Post pobrano z: Evolution of img: Gif without the GIF

Colin Bendell writes about a new and particularly weird addition to Safari Technology Preview in this excellent post about the evolution of animated images on the web. He explains how we can now add an MP4 file directly to the source of an img tag. That would look something like this:

<img src="video.mp4"/>

The idea is that that code would render an image with a looping video inside. As Colin describes, this provides a host of performance benefits:

Animated GIFs are a hack. […] But they have become an awesome tool for cinemagraphs, memes, and creative expression. All of this awesomeness, however, comes at a cost. Animated GIFs are terrible for web performance. They are HUGE in size, impact cellular data bills, require more CPU and memory, cause repaints, and are battery killers. Typically GIFs are 12x larger files than H.264 videos, and take 2x the energy to load and display in a browser. And we’re spending all of those resources on something that doesn’t even look very good – the GIF 256 color limitation often makes GIF files look terrible…

By enabling video content in img tags, Safari Technology Preview is paving the way for awesome Gif-like experiences, without the terrible performance and quality costs associated with GIF files. This functionality will be fantastic for users, developers, designers, and the web. Besides the enormous performance wins that this change enables, it opens up many new use cases that media and ecommerce businesses have been yearning to implement for years. Here’s hoping the other browsers will soon follow.

This seems like a weird hack but, after mulling it over for a second, I get how simple and elegant a solution this is. It also sort of means that other browsers won’t have to support WebP in the future, too.

Direct Link to Article — Permalink


Evolution of img: Gif without the GIF is a post from CSS-Tricks

Calendar with CSS Grid

Post pobrano z: Calendar with CSS Grid

Here’s a nifty post by Jonathan Snook where he walks us through how to make a calendar interface with CSS Grid and there’s a lot of tricks in here that are worth digging into a little bit more, particularly where Jonathan uses grid-auto-flow: dense which will let Grid take the wheels of a design and try to fill up as much of the allotted space as possible.

As I was digging around, I found a post on Grid’s auto-placement algorithm by Ian Yates which kinda fleshes things out more succinctly. Might come in handy.

Oh, and we have an example of a Grid-based calendar in our ongoing collection of CSS Grid starter templates.

Direct Link to Article — Permalink


Calendar with CSS Grid is a post from CSS-Tricks

Keeping Parent Visible While Child in :focus

Post pobrano z: Keeping Parent Visible While Child in :focus

Say we have a <div>.

We only want this div to be visible when it’s hovered, so:

div:hover { 
  opacity: 1; 
}

We need focus styles as well, for accessibility, so:

div:hover,
div:focus { 
  opacity: 1; 
}

But div’s can’t be focused on their own, so we’ll need:

<div tabindex="0">
</div>

There is content in this div. Not just text, but links as well.

<div tabindex="0">
  <p>This little piggy went to market.</p>
  <a href="#market">Go to market</a>
</div>

This is where it gets tricky.

As soon as focus moves from the div to the anchor link inside it, the div is no longer in focus, which leads to this weird and potentially confusing situation:

In this example, :hover reveals the div, including the link inside. Focusing the div also works, but as soon as you tab to move focus to the link, everything disappears. The link inside can recieve focus, but it’s visually hidden because the div parent is visually hidden.

One solution here is to ensure that the div remains visible when anything inside of it is focused. New CSS has our back here:

div:hover,
div:focus,
div:focus-within { 
  opacity: 1; 
}

GIF working

But browser support isn’t great for :focus-within. If it was perfect, this is all we would need. In fact we wouldn’t even need :focus because :focus-within handles that also.

But until then, we might need JavaScript to help. How you actually approach this depends, but the idea would be something like…

  1. When a element comes into focus…
  2. If the parent of that element is also focusable, make sure it is visible
  3. When the link leaves focus…
  4. Whatever you did to make sure the parent visible is reversed

There is a lot to consider here, like which elements you actually want to watch, how to make them visible, and how far up the tree you want to go.

Something like this is a very basic approach:

var link = document.querySelector(".deal-with-focus-with-javascript");

link.addEventListener("focus", function() {
  link.parentElement.classList.add("focus");
});
link.addEventListener("blur", function() {
  link.parentElement.classList.remove("focus");
});

See the Pen :focus-within helpful a11y thing by Chris Coyier (@chriscoyier) on CodePen.


Keeping Parent Visible While Child in :focus is a post from CSS-Tricks

Animating Border

Post pobrano z: Animating Border

Transitioning border for a hover state. Simple, right? You might be unpleasantly surprised.

The Challenge

The challenge is simple: building a button with an expanding border on hover.

This article will focus on genuine CSS tricks that would be easy to drop into any project without having to touch the DOM or use JavaScript. The methods covered here will follow these rules

  • Single element (no helper divs, but psuedo-elements are allowed)
  • CSS only (no JavaScript)
  • Works for any size (not restricted to a specific width, height, or aspect ratio)
  • Supports transparent backgrounds
  • Smooth and performant transition

I proposed this challenge in the Animation at Work Slack and again on Twitter. Though there was no consensus on the best approach, I did receive some really clever ideas by some phenomenal developers.

Method 1: Animating border

The most straightforward way to animate a border is… well, by animating border.

.border-button {
  border: solid 5px #FC5185;
  transition: border-width 0.6s linear;
}

.border-button:hover { border-width: 10px; }

See the Pen CSS writing-mode experiment by Shaw (@shshaw) on CodePen.

Nice and simple, but there are some big performance issues.

Since border takes up space in the document’s layout, changing the border-width will trigger layout. Nearby elements will shift around because of the new border size, making browser reposition those elements every frame of the animation unless you set an explicit size on the button.

As if triggering layout wasn’t bad enough, the transition itself feels “stepped”. I’ll show why in the next example.

Method 2: Better border with outline

How can we change the border without triggering layout? By using outline instead! You’re probably most familiar with outline from removing it on :focus styles (though you shouldn’t), but outline is an outer line that doesn’t change an element’s size or position in the layout.

.border-button {
  outline: solid 5px #FC5185;
  transition: outline 0.6s linear;
  margin: 0.5em; /* Increased margin since the outline expands outside the element */
}

.border-button:hover { outline-width: 10px; }

A quick check in Dev Tools’ Performance tab shows the outline transition does not trigger layout. Regardless, the movement still seems stepped because browsers are rounding the border-width and outline-width values so you don’t get sub-pixel rendering between 5 and 6 or smooth transitions from 5.4 to 5.5.

Strangely, Safari often doesn’t render the outline transition and occasionally leaves crazy artifacts.

border artifact in safari

Method 3: Cut it with clip-path

First implemented by Steve Gardner, this method uses clip-path with calc to trim the border down so on hover we can transition to reveal the full border.

.border-button {  
  /* Full width border and a clip-path visually cutting it down to the starting size */
  border: solid 10px #FC5185; 
  clip-path: polygon( 
    calc(0% + 5px) calc(0% + 5px), /* top left */
    calc(100% - 5px) calc(0% + 5px), /* top right */
    calc(100% - 5px) calc(100% - 5px), /* bottom right */
    calc(0% + 5px) calc(100% - 5px) /* bottom left */
  );
  transition: clip-path 0.6s linear;
}

.border-button:hover {
  /* Clip-path spanning the entire box so it's no longer hiding the full-width border. */
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

clip-path technique is the smoothest and most performant method so far, but does come with a few caveats. Rounding errors may cause a little unevenness, depending on the exact size. The border also has to be full size from the start, which may make exact positioning tricky.

Unfortunately there’s no IE/Edge support yet, though it seems to be in development. You can and should encourage Microsoft’s team to implement those features by voting for masks/clip-path to be added.

Method 4: linear-gradient background

We can simulate a border using a clever combination of multiple linear-gradient backgrounds properly sized. In total we have four separate gradients, one for each side. The background-position and background-size properties get each gradient in the right spot and the right size, which can then be transitioned to make the border expand.

.border-button {
  background-repeat: no-repeat;
  
  /* background-size values will repeat so we only need to declare them once */
  background-size: 
    calc(100% - 10px) 5px, /* top & bottom */
    5px calc(100% - 10px); /* right & left */
  
  background-position: 
    5px 5px, /* top */
    calc(100% - 5px) 5px, /* right */
    5px calc(100% - 5px), /* bottom */
    5px 5px; /* left */
  
  /* Since we're sizing and positioning with the above properties, we only need to set up a simple solid-color gradients for each side */
  background-image: 
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185);
  
  transition: all 0.6s linear;
  transition-property: background-size, background-position;
}

.border-button:hover {
  background-position: 0 0, 100% 0, 0 100%, 0 0;
  background-size: 100% 10px, 10px 100%, 100% 10px, 10px 100%;
}

This method is quite difficult to set up and has quite a few cross-browser differences. Firefox and Safari animate the faux-border smoothly, exactly the effect we’re looking for. Chrome’s animation is jerky and even more stepped than the outline and border transitions. IE and Edge refuse to animate the background at all, but they do give the proper border expansion effect.

Method 5: Fake it with box-shadow

Hidden within box-shadow’s spec is a fourth value for spread-radius. Set all the other length values to 0px and use the spread-radius to build your border alternative that, like outline, won’t affect layout.

.border-button {
  box-shadow: 0px 0px 0px 5px #FC5185;
  transition: box-shadow 0.6s linear;
  margin: 0.5em; /* Increased margin since the box-shado expands outside the element, like outline */
}

.border-button:hover { box-shadow: 0px 0px 0px 10px #FC5185; }

The transition with box-shadow is adequately performant and feels much smoother, except in Safari where it’s snapping to whole-values during the transition like border and outline.

Pseudo-Elements

Several of these techniques can be modified to use a pseudo-element instead, but pseudo-elements ended up causing some additional performance issues in my tests.

For the box-shadow method, the transition occasionally triggered paint in a much larger area than necessary. Reinier Kaper pointed out that a pseudo-element can help isolate the paint to a more specific area. As I ran further tests, box-shadow was no longer causing paint in large areas of the document and the complication of the pseudo-element ended up being less performant. The change in paint and performance may have been due to a Chrome update, so feel free to test for yourself.

I also could not find a way to utilize pseudo-elements in a way that would allow for transform based animation.

Why not transform: scale?

You may be firing up Twitter to helpfully suggest using transform: scale for this. Since transform and opacity are the best style properties to animate for performance, why not use a pseudo-element and have the border scale up & down?

.border-button {
  position: relative;
  margin: 0.5em;
  border: solid 5px transparent;
  background: #3E4377;
}

.border-button:after {
  content: '';
  display: block;
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  border: solid 10px #FC5185;
  margin: -15px;
  z-index: -1;
  transition: transform 0.6s linear;
  transform: scale(0.97, 0.93);
}

.border-button:hover::after { transform: scale(1,1); }

There are a few issues:

  1. The border will show through a transparent button. I forced a background on the button to show how the border is hiding behind the button. If your design calls for buttons with a full background, then this could work.
  2. You can’t scale the border to specific sizes. Since the button’s dimensions vary with the text, there’s no way to animate the border from exactly 5px to 10px using only CSS. In this example I’ve done some magic-numbers on the scale to get it to appear right, but that won’t be universal.
  3. The border animates unevenly because the button’s aspect ratio isn’t 1:1. This usually means the left/right will appear larger than the top/bottom until the animation completes. This may not be an issue depending on how fast your transition is, the button’s aspect ratio, and how big your border is.

If your button has set dimensions, Cher pointed out a clever way to calculate the exact scales needed, though it may be subject to some rounding errors.

Beyond CSS

If we loosen our rules a bit, there are many interesting ways you can animate borders. Codrops consistently does outstanding work in this area, usually utilizing SVGs and JavaScript. The end results are very satisfying, though they can be a bit complex to implement. Here are a few worth checking out:

Conclusion

There’s more to borders than simply border, but if you want to animate a border you may have some trouble. The methods covered here will help, though none of them are a perfect solution. Which you choose will depend on your project’s requirements, so I’ve laid out a comparison table to help you decide.

My recommendation would be to use box-shadow, which has the best overall balance of ease-of-implementation, animation effect, performance and browser support.

Do you have another way of creating an animated border? Perhaps a clever way to utilize transforms for moving a border? Comment below or reach me on Twitter to share your solution to the challenge.

Special thanks to Martin Pitt, Steve Gardner, Cher, Reinier Kaper, Joseph Rex, David Khourshid, and the Animation at Work community.


Animating Border is a post from CSS-Tricks