Web Standards

Creating a VS Code Theme

Css Tricks - Fri, 06/08/2018 - 3:13am

Everyone has special and perhaps, particular, tastes when it comes to their code editor. There are literally thousands of themes out there, and for good reason: a thing of beauty and enhancement to productivity for one can be a hindrance to another.

It’s been an item on my bucket list to create my own theme. I was coding very late the one night, well into the small hours of the morning. Everyone in my house was sleeping and so, as usual, the only light was the glow of my screen. I know it’s not necessarily healthy to code like this, but it’s literally the time I’m most productive: there are minimal distractions, I’m not dealing with work stuff, family stuff, friend stuff, or puppy stuff. I can focus.

I had some preferences set for the theme I had been using and, though they all worked well for daytime or plane rides, I always felt like something was missing for late night coding sessions. I decided it was time to craft my own theme.

We'll talk first about the general process for creating a theme in case you'd like to create one of your own, and then we'll dive into some of the research and testing that went into mine in particular to peek into the process.

Fire It Up

Before you do anything, you’re going to install vsce (short for Visual Studio Code Extensions) and establish yourself as a publisher. All of the instructions to do so are here. I know it looks like a lot, but it takes anywhere from 5-10 minutes, and then you'll never have to do it again, for any extension you create.

Now that you’ve got that under your belt, here are the steps you need to start work.

First, you need to run:

npm install -g yo generator-code

This makes the generator globally available on your machine (meaning you can now create a theme in any directory). You can then execute this command to kick off your theme:

yo code

You will be prompted by a screen that looks like this:

Note that I’ve used the arrows here to navigate to the “New Color Theme” option. Note also that this is how you’d want to make any other extension.

When selecting this, it asks if this is a new theme or if we want to import from an existing one. We want to create a new one.

Next, you'll have to answer a few other questions, including:

  • What’s the extension's name?
  • What is the the identifier? (I just went with the name, that’s probably typical.)
  • What is the the description? (I just put something silly in initially. Don’t worry, you can update this in your package.json in the future.)
  • What's the publisher's name? (See earlier instructions.)
  • What name should be shown to the user? (I used the same as the extension name.)
  • Is this theme dark, light, or high contrast?

It will set you up with a base theme to start skinning your color preferences. The full scoop and all the details are here. More details about themes in general are here.

Test Drive

We have our base theme and we have some concepts for the palette. So, how do we test it out? When you open the directory with your theme, you can press fn + f5 on Mac (or just f5 on Windows) and a new window immediately pops open where you can test your theme! You’ll see in the original theme window that you now have a little control panel where you can reload, pause, and stop. Don’t forget to save before you do!

OK, now that you have the other window open, hit Command + Shift + P to get the command explorer. In there type, "Developer: Inspect TM Scopes" and you’ll see a prompt come up that allows you to look through all the tags and attributes: it will tell you their color, their font styles, and how you need to target it.

There is one problem, though. There are a lot of things in the editor you can’t target because VS Code will interpret that as you trying to drive the rest of the editor (i.e. the file viewer, the terminal, and the search boxes). Here are the two ways I found to figure out the rest of the scopes:

  • This page is extremely helpful in understanding some of the base things you need to configure. In fact, you might want to start with some of these.
  • There are DevTools! You can open them the same way you do with Chrome: Command + Option + I. What I did was look for the color in the computed styles and look them up in the text editor to target them. You’ll notice that the default in the DevTools is RGBA, so you will have to Shift + click on the color to change it’s format until you get to the equivalent hex values. I could then scan through the matching colors in my theme json until I found the matching value and change it.
Another Small Tip!

When I first started to develop the theme, I thought I would try forking someone else's theme as a starting point. I tried out Wes Bos' Cobalt Two. Though I didn't end up using it, one thing he had that I found valuable was a demos directory with examples of all of a whole slew of different languages. I started by moving his over, but realized quickly that the files weren’t long enough for my testing needs. So I created my own. In the course of correcting issues people filed, I also created a React stateless functional component example, a Ruby example, and of course I created a .vue single file component 😀 This is also helpful in maintenance because if people are seeing an issue on a file type I previously didn't test on, they can PR the file into the demos directory, and I can target what they're seeing. It makes duplication and testing really simple.

Research

Research for a code theme? Isn’t that over the top? Probably! But I was genuinely curious: what would work best for legibility for the vast majority of people, while still being something I liked?

Color and contrast

The first step was considering accessibility. I always liked how solarized themes made legibility a central theme to their palettes. I read about color retention and accessibility, and it turns out that men have a really high incidence of colorblindness (around 8% for men, 1% for women). The majority of programmers are men, so even though I am not colorblind, it was a no-brainer to craft the theme at least partially around including those with colorblindness. The most typical is red/green deficiency so I found a few good ways to test with my favorite being, funnily enough, a bit manual.

I originally started by testing random images to see if I could discern a pattern that I could match. One thing I noticed while testing was that complementary colors seemed to perform the best across all tests. However, if three colors needed to be tested at once, a triad color composition also produced good results.

If you’re unfamiliar with color relationships, Adobe Color CC (previously Kuler) makes it easier to visualize and you can even create a color palette directly in the editor.

It's extremely important to know that a color is only a color in reference to another color. This is part of what makes crafting a color theme so difficult. Color isn't static, it's all about relationships. You’re probably a little familiar with this in terms of accessibility. A light green on a black may be accessible, but when you change it to a white background it no longer is.

Accessibility in color can be measured with a number of tools. Here are some of my favorites:

It’s also really nice to set up your palette for accessibility from the start. Color Safe is a great tool that helps with that.

I cover more details about color and perception in this post: A Nerd's Guide to Color on the Web.

Colors and Reading Comprehension

Another piece of this was learning which colors, if any, had an effect on reading comprehension. In some studies, it’s been shown that black text on white background, such as used in some light themes, can be difficult for comprehension. The theory is that the use of overlays to change the text color has improved cognitive awareness for many, especially those with dyslexia and autism. However, these studies are controversial and it's inconclusive whether the overlays are effective in comprehension or a preference.

There is a syndrome called Irlen, or Scotopic Sensitivity Syndrome (SSS) that is known to interfere with the ability to discern letters and words. It is a visual perception disability on the magnocellular level — the visual pathway that can help with scanning and comprehension. This has been thought to be connected to — reading with impairment under certain types of light, and some think it can affect up to 50% of people (again, this figure is controversial and inconclusive).

We’re still learning about SSS, but there are some studies that color overlays can help focus attention to the text and reduce eyestrain. The colors found to increase readability and contrast so far for those with SSS have been beige, goldenrod, green, pink and blue. Blue has shown thus far to have the strongest link for people with Reading Disability and Attention Deficit/Hyperactivity Disorder.

Despite the fact that these studies haven't reached statistical signifigance from what I can gather, I couldn't find evidence that there was any harm in following them, and it seemed safest to keep them in mind while developing the theme. I chose a dark theme with blue as the primary color and used the other colors that tested well in supportive and contrasting roles throughout the theme.

Other Theme Inspiration

There were a few palettes I looked at for inspiration. For example, I did an exploratory study into what kind of tone I wanted.

  • Palenight Material: the reds and purples in my theme started with this one, and I adjusted the purples values.
  • Dracula: this theme's base was a bit darker and provided contrast to the pastels I wanted in my theme.
  • Panda: I borrowed the turquoise color and adjusted it a bit.

I also looked at the work of Maggie Appleton quite a bit. I especially like her work on Egghead.io, which is amazing on every level.

Those greens and oranges are where I started with my palette. I made adjustments while working on accessibility. The blackest blue-black that’s in the lower right of the image became the base of my main background.

Decisions, Decisions

There were a lot of decisions to make at this point. Thankfully, my research was done. Remember, I wanted pastels, like the ones used in the Material Palenight, Panda and Dracula Dark themes. Specifically, I wanted to use beige, goldenrod, green, pink and blue based on what I had read in the research phase. But the most important part to me was contrast across the color spectrum. That’s what I felt some of the other themes lacked, even if they nailed the colors.

I went to work, creating blue and a golden color as the base standard for working across the color spectrum.

I used purple for keywords that are informative but I didn’t want to call out as strongly — if you’re trying to create contrast, you also need to consider what colors to make subtle so that it gives attention to what’s most important. If everything is important, nothing is. I also wanted to offset the fact that the purple had a shallow contrast by making it different in some other way. I did this with the use of italics. Some people like that, some hate it. I decided to buy a font called Dank Mono, similar to Operator Mono, or Fira Code (the latter being the free open source version), partially because I enjoyed the presentation of the italic glyphs. They also have font ligatures, which can be quite stylish. You set them in your user preferences with "editor.fontLigatures": true. Some people aren't super into the italics, though, so I created a no italic version that people could switch to if it bothered them.

I wanted to call out state/data strongly because it tends to be important for me when when scanning code. I started with red because I had seen that in many other themes, but I couldn’t get away from the fact that my eyes would only go there and the fact that red is often associated with error states. So, instead, I used the strongest color against the background I had chosen, white, and italicized it to offset it even more. It also has the benefit of being a midpoint between blue and gold. I saved the red/orange distinction for React components, which needed to have some separation from the standard HTML elements.

Contrast is a zero-sum game: if everything is important, nothing is. I tried my best to be sure things that were conceptually similar or could fall back, did so that strong contrast was intentional and everything didn't turn into a rainbow, because that hurts your ability to scan the document.

One such decision was to keep the sidebar contrast low in order to keep the focus on the editor. I found that if I tried to bring the contrast up in other parts of my editor, my eyes actually began to hurt. This can be a challenging thing about some accessibility- because not all humans are the same, things like color and font can become a spectrum rather than a hard rule.

After running a lot of tests, the compromise I decided on was to keep the theme to what I myself could use without strain, and update the readme with the preferences I would recommend for someone who was different from me and needed the contrast levels to be higher. If you go into your user settings in VS Code (Code > Preferences > Settings), in the righthand pane, you can add your own customizations. With the help of some people in the community who filed issues because they wanted this feature, we arrived at these possible color preference updates for those who need the contrast:

"workbench.colorCustomizations": { "activityBar.background": "#000C1D", "activityBar.border": "#102a44", "sideBar.background": "#001122", "sideBar.border": "#102a44", "sideBar.foreground": "#8BADC1" },

You can actually drop any colors in here, this was just a suggestion for a starting point based on the existing theme colors. These workbench color customizations are really handy, they allow people to use a theme, and then make small tweaks as they feel they need them. If you're using a theme and it's allllmost perfect but not quite, you can always make small changes this way.

There were hundreds of other small decisions I made over the course of creating it (and am still making now that I'm maintaining it), but after I had made a good amount of tweaks, I would check my work against the colorblind simulator. It wasn’t terribly easy getting it to work right in every language for every setting, but I did my best. This is where that demo folder came in really handy. Now that it's launched, if someone needs particular language support, I can encourage them to PR the folder so that I can support it.

Here's an example Angular file:

...and here are some of the tests I ran to determine if there was enough contrast. Remember, what I was looking for was contrast across the color spectrum for meaningful distinctions, and slight contrast for things that require less attention:

It took a good amount of tests to get something that didn't become monochrome, especially across languages. The amount of color combinations possible are a bit endless, and it's pretty difficult to make something that works perfectly in every scenario. That's why I spent a lot of time crafting the demo folder and making small tweaks to try to cover as much ground as possible.

Bugfixes

I launched it! Everyone party! 🎉

The most helpful thing to me were the contributions of people using the theme and letting me know their pain points by logging issues in the GitHub repo. It’s hard to see every failure scenario across a theme, and have so far had 16 subsequent releases to fix over 50 bugs so far, some with help from the community. The more people who let me know what they’re seeing, the better the theme gets. Not everything gets in of course — there are times when people want things that conflict with other requests, so I have to make a judgement call in some cases. Still, this this is rare and the majority of feedback so far have been very clean-cut and actionable.

That’s it! If you'd like to check out the theme, it's available here for free. I hope you found this useful, either for background into the theme and the decisions that were made, or for a process in creating your own.

The post Creating a VS Code Theme appeared first on CSS-Tricks.

World wide wrist

Css Tricks - Thu, 06/07/2018 - 7:48am

After all the hubbub with WWDC over the past couple of days, Ethan Marcotte is excited about the news that the Apple Watch will be able to view web content.

He writes:

If I had to guess, I’d imagine some sort of “reader mode” is coming to the Watch: in other words, when you open a link on your Watch, this minified version of WebKit wouldn’t act like a full browser. Instead of rendering all your scripts, styles, and layout, mini-WebKit would present a stripped-down version of your web page. If that’s the case, then Jen Simmons’s suggestion is spot-on: it just got a lot more important to design from a sensible, small screen-friendly document structure built atop semantic HTML.

But who knows! I could be wrong! Maybe it’s a more capable browser than I’m assuming, and we’ll start talking about best practices for layout, typography, and design on watches.

I had this inkling for a long while that there wouldn’t ever be a browser in the Watch due to its constraints, but instead I hoped that there might be a surge of methods to read web content aloud via some sort of voice interface. "Siri, read me the latest post from James’ blog," is probably nightmare fuel for most people but I was sort of holding out for devices like this to access the web via audio.

Another interesting aside is that both Safari OSX and iOS have had a reader mode for a long time now, but have it as an option enabled by the user while viewing the content. Bypassing the user-enabled option would be the difference on watchOS and where our structured, semantic chops are put to task.

Direct Link to ArticlePermalink

The post World wide wrist appeared first on CSS-Tricks.

Manipulating Pixels Using Canvas

Css Tricks - Thu, 06/07/2018 - 3:27am

Modern browsers support playing video via the <video> element. Most browsers also have access to webcams via the MediaDevices.getUserMedia() API. But even with those two things combined, we can’t really access and manipulate those pixels directly.

Fortunately, browsers have a Canvas API that allows us to draw graphics using JavaScript. We can actually draw images to the <canvas> from the video itself, which gives us the ability to manipulate and play with those pixels.

Everything you learn here about how to manipulate pixels will give you a foundation to work with images and videos of any kind or any source, not just canvas.

Adding an image to canvas

Before we start playing with video, let’s look at adding an image to canvas.

<img id="SourceImage" src="image.jpg"> <div class="video-container"> <canvas id="Canvas" class="video"></canvas> </div>

We created an image element that represents the image that is going to be drawn on the canvas. Alternatively we could use the Image object in JavaScript.

var canvas; var context; function init() { var image = document.getElementById('SourceImage'); canvas = document.getElementById('Canvas'); context = canvas.getContext('2d'); drawImage(image); // Or // var image = new Image(); // image.onload = function () { // drawImage(image); // } // image.src = 'image.jpg'; } function drawImage(image) { // Set the canvas the same width and height of the image canvas.width = image.width; canvas.height = image.height; context.drawImage(image, 0, 0); } window.addEventListener('load', init);

The code above draws the whole image onto the canvas.

See the Pen Paint image on canvas by Welling Guzman (@wellingguzman) on CodePen.

Now we can start playing with those pixels!

Updating the image data

The image data on the canvas allows us to manipulate and change the pixels.

The data property is an ImageData object with three properties — the width, height and data/ all of which represent those things based on the original image. All these properties are readonly. The one we care about is data, n one-dimensional array represented by an Uint8ClampedArray object, containing the data of each pixel in a RGBA format.

Although the data property is readonly, it doesn’t mean we cannot change its value. It means we cannot assign another array to this property.

// Get the canvas image data var imageData = context.getImageData(0, 0, canvas.width, canvas.height); image.data = new Uint8ClampedArray(); // WRONG image.data[1] = 0; // CORRECT

What values does the Uint8ClampedArray object represent, you may ask. Here is the description from MDN:

The Uint8ClampedArray typed array represents an array of 8-bit unsigned integers clamped to 0-255; if you specified a value that is out of the range of [0,255], 0 or 255 will be set instead; if you specify a non-integer, the nearest integer will be set. The contents are initialized to 0. Once established, you can reference elements in the array using the object's methods, or using standard array index syntax (that is, using bracket notation)

In short, this array stores values ranging from 0 to 255 in each position, making this the perfect solution for the RGBA format, as each part it is represented by 0 to 255 values.

RGBA colors

Colors can be represented by RGBA format, which is a combination of Red, Green and Blue. The A represents the alpha value which is the opacity of the color.

Each position in the array represents a color (pixel) channel value.

  • 1st position is the Red value
  • 2nd position is the Green value
  • 3rd position is the Blue value
  • 4th position is the Alpha value
  • 5th position is the next pixel Red value
  • 6th position is the next pixel Green value
  • 7th position is the next pixel Blue value
  • 8th position is the next pixel Alpha value
  • And so on...

If you have a 2x2 image, then we have a 16 position array (2x2 pixels x 4 value each).

The 2x2 image zoomed up close

The array will be represented as shown below:

// RED GREEN BLUE WHITE [ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255] Changing the pixel data

One of the quickest things we can do is set all pixels to white by changing all RGBA values to 255.

// Use a button to trigger the "effect" var button = document.getElementById('Button'); button.addEventListener('click', onClick); function changeToWhite(data) { for (var i = 0; i < data.length; i++) { data[i] = 255; } } function onClick() { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); changeToWhite(imageData.data); // Update the canvas with the new data context.putImageData(imageData, 0, 0); }

The data will be passed as reference, which means any modification we make to it, it will change the value of the argument passed.

Inverting colors

A nice effect that doesn’t require much calculation is inverting the colors of an image.

Inverting a color value can be done using XOR operator (^) or this formula 255 - value (value must be between 0-255).

function invertColors(data) { for (var i = 0; i < data.length; i+= 4) { data[i] = data[i] ^ 255; // Invert Red data[i+1] = data[i+1] ^ 255; // Invert Green data[i+2] = data[i+2] ^ 255; // Invert Blue } } function onClick() { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); invertColors(imageData.data); // Update the canvas with the new data context.putImageData(imageData, 0, 0); }

We are incrementing the loop by 4 instead of 1 as we did before, so we can from pixel to pixel that each fill 4 elements in the array.

The alpha value has not effect on inverting colors, so we skip it.

Brightness and contrast

Adjusting the brightness of an image can be done using the next formula: newValue = currentValue + 255 * (brightness / 100).

  • brightness must be between -100 and 100
  • currentValue is the current light value of either Red, Green or Blue.
  • newValue is the result of the current color light plus brightness

Adjusting the contrast of an image can be done with this formula:

factor = (259 * (contrast + 255)) / (255 * (259 - contrast)) color = GetPixelColor(x, y) newRed = Truncate(factor * (Red(color) - 128) + 128) newGreen = Truncate(factor * (Green(color) - 128) + 128) newBlue = Truncate(factor * (Blue(color) - 128) + 128)

The main calculation is getting the contrast factor that will be applied to each color value. Truncate is a function that make sure the value stay between 0 and 255.

Let’s write these functions into JavaScript:

function applyBrightness(data, brightness) { for (var i = 0; i < data.length; i+= 4) { data[i] += 255 * (brightness / 100); data[i+1] += 255 * (brightness / 100); data[i+2] += 255 * (brightness / 100); } } function truncateColor(value) { if (value < 0) { value = 0; } else if (value > 255) { value = 255; } return value; } function applyContrast(data, contrast) { var factor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 - contrast)); for (var i = 0; i < data.length; i+= 4) { data[i] = truncateColor(factor * (data[i] - 128.0) + 128.0); data[i+1] = truncateColor(factor * (data[i+1] - 128.0) + 128.0); data[i+2] = truncateColor(factor * (data[i+2] - 128.0) + 128.0); } }

In this case you don't need the truncateColor function as Uint8ClampedArray will truncate these values, but for the sake of translating the algorithm we added that in.

One thing to keep in mind is that, if you apply a brightness or contrast, there’s no way back to the previous state as the image data is overwritten. The original image data must be stored separately for reference if we want to reset to the original state. Keeping the image variable accessible to other functions will be helpful as you can use that image instead to redraw the canvas with the original image.

var image = document.getElementById('SourceImage'); function redrawImage() { context.drawImage(image, 0, 0); }

Using videos

To make it work with videos, we are going to take our initial image script and HTML code and make some small changes.

HTML

Change the Image element with a video element by replacing this line:

<img id="SourceImage" src="image.jpg">

...with this:

<video id="SourceVideo" src="video.mp4"></video> JavaScript

Replace this line:

var image = document.getElementById('SourceImage');

...with this:

var video = document.getElementById('SourceVideo');

To start working with the video, we have to wait until the video can be played.

video.addEventListener('canplay', function () { // Set the canvas the same width and height of the video canvas.width = video.videoWidth; canvas.height = video.videoHeight; // Play the video video.play(); // start drawing the frames drawFrame(video); });

The event canplay is triggered when enough data is available that the media can be played, at least for a couple of frames.

We cannot see any of the video displayed on the canvas because we are only displaying the first frame. We must execute drawFrame every n milliseconds to keep up with the video frames rate.

Inside drawFrame we call drawFrame again every 10ms.

function drawFrame(video) { context.drawImage(video, 0, 0); setTimeout(function () { drawFrame(video); }, 10); }

After we execute drawFrame, we create a loop executing drawFrame every 10ms — enough time to keep the video in sync in the canvas.

Adding the effect to the video

We can use the same function we created before for inverting colors:

function invertColors(data) { for (var i = 0; i < data.length; i+= 4) { data[i] = data[i] ^ 255; // Invert Red data[i+1] = data[i+1] ^ 255; // Invert Green data[i+2] = data[i+2] ^ 255; // Invert Blue } }

And add it into the drawFrame function:

function drawFrame(video) { context.drawImage(video, 0, 0); var imageData = context.getImageData(0, 0, canvas.width, canvas.height); invertColors(imageData.data); context.putImageData(imageData, 0, 0); setTimeout(function () { drawFrame(video); }, 10); }

We can add a button and toggle the effects:

function drawFrame(video) { context.drawImage(video, 0, 0); if (applyEffect) { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); invertColors(imageData.data); context.putImageData(imageData, 0, 0); } setTimeout(function () { drawFrame(video); }, 10); }

Using camera

We are going to keep the same code we use for video with the only different is that we are going to change the video stream from a file to the camera stream using MediaDevices.getUserMedia

MediaDevices.getUserMedia is the new API deprecating the previous API MediaDevices.getUserMedia(). There's still browser support for the old version and some browser do not support the new version and we have to resort to polyfill to make sure the browser support one of them

First, remove the src attribute from the video element:

<video id="SourceVideo"><code></pre> <pre rel="JavaScript"><code class="language-javascript">// Set the source of the video to the camera stream function initCamera(stream) { video.src = window.URL.createObjectURL(stream); } if (navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({video: true, audio: false}) .then(initCamera) .catch(console.error) ); }

Live Demo

Effects

Everything we’ve covered so far is the foundation we need in order to create different effects on a video or image. There are a lot of different effects that we can use by transforming each color independently.

GrayScale

Converting a color to a grayscale can done in different ways using different formulas/techniques, to avoid getting too deep into the subject I will show you five of the formulas based on the GIMP desaturate tool and Luma:

Gray = 0.21R + 0.72G + 0.07B // Luminosity Gray = (R + G + B) ÷ 3 // Average Brightness Gray = 0.299R + 0.587G + 0.114B // rec601 standard Gray = 0.2126R + 0.7152G + 0.0722B // ITU-R BT.709 standard Gray = 0.2627R + 0.6780G + 0.0593B // ITU-R BT.2100 standard

What we want to find using these formulas is the brightness intensity level of each pixel color. The value will range from 0 (black) to 255 (white). These values will create a grayscale (black and white) effect.

This means that the brightest color will be closest to 255 and the darkest color closest to 0.

Live Demo

Duotones

The difference between duotone effect and grayscale effect are the two colors being used. On grayscale you have a gradient from black to white, while on duotone you can have a gradient from any color to any other color, blue to pink as an example.

Using the intensity value of the grayscale, we can replace this from the gradient values.

We need to create a gradient from ColorA to ColorB.

function createGradient(colorA, colorB) { // Values of the gradient from colorA to colorB var gradient = []; // the maximum color value is 255 var maxValue = 255; // Convert the hex color values to RGB object var from = getRGBColor(colorA); var to = getRGBColor(colorB); // Creates 256 colors from Color A to Color B for (var i = 0; i <= maxValue; i++) { // IntensityB will go from 0 to 255 // IntensityA will go from 255 to 0 // IntensityA will decrease intensity while instensityB will increase // What this means is that ColorA will start solid and slowly transform into ColorB // If you look at it in other way the transparency of color A will increase and the transparency of color B will decrease var intensityB = i; var intensityA = maxValue - intensityB; // The formula below combines the two color based on their intensity // (IntensityA * ColorA + IntensityB * ColorB) / maxValue gradient[i] = { r: (intensityA*from.r + intensityB*to.r) / maxValue, g: (intensityA*from.g + intensityB*to.g) / maxValue, b: (intensityA*from.b + intensityB*to.b) / maxValue }; } return gradient; } // Helper function to convert 6digit hex values to a RGB color object function getRGBColor(hex) { var colorValue; if (hex[0] === '#') { hex = hex.substr(1); } colorValue = parseInt(hex, 16); return { r: colorValue >> 16, g: (colorValue >> 8) & 255, b: colorValue & 255 } }

In short, we are creating an array of color values from Color A decreasing the intensity while going to Color B and increasing its intensity.

From #0096ff to #ff00f0 Zoomed representation of the color transition var gradients = [ {r: 32, g: 144, b: 254}, {r: 41, g: 125, b: 253}, {r: 65, g: 112, b: 251}, {r: 91, g: 96, b: 250}, {r: 118, g: 81, b: 248}, {r: 145, g: 65, b: 246}, {r: 172, g: 49, b: 245}, {r: 197, g: 34, b: 244}, {r: 220, g: 21, b: 242}, {r: 241, g: 22, b: 242}, ];

Above there is an example of a gradient of 10 colors values from #0096ff to #ff00f0.

Grayscale representation of the color transition

Now that we have the grayscale representation of the image, we can use it to map it to the duotone gradient values.

The duotone gradient has 256 colors while the grayscale has also 256 colors ranging from black (0) to white (255). That means a grayscale color value will map to a gradient element index.

var gradientColors = createGradient('#0096ff', '#ff00f0'); var imageData = context.getImageData(0, 0, canvas.width, canvas.height); applyGradient(imageData.data); for (var i = 0; i < data.length; i += 4) { // Get the each channel color value var redValue = data[i]; var greenValue = data[i+1]; var blueValue = data[i+2]; // Mapping the color values to the gradient index // Replacing the grayscale color value with a color for the duotone gradient data[i] = gradientColors[redValue].r; data[i+1] = gradientColors[greenValue].g; data[i+2] = gradientColors[blueValue].b; data[i+3] = 255; }

Live Demo

Conclusion

This topic can go more in depth or explain more effects. The homework for you is to find different algorithms you can apply to these skeleton examples.

Knowing how the pixels are structured on a canvas will allow you to create an unlimited number of effects, such as sepia, color blending, a green screen effect, image flickering/glitching, etc.

You can even create effects on the fly without using an image or a video:

The post Manipulating Pixels Using Canvas appeared first on CSS-Tricks.

Headless CMS: The Developers’ Best Friend

Css Tricks - Thu, 06/07/2018 - 3:24am

(This is a sponsored post.)

Your current CMS sucks! You know that for some time already but have not decided yet what your next solution should be.

You've noticed all the buzz around headless CMS but you're still not sure what is in it for you and how it can solve all your woes.

What is the difference between traditional on-premise CMS with REST API and true API-first cloud based CMS? How does headless CMS fit to your scenarios? What it brings being language agnostic for you?

Explore the new possibilities unlocked by the headless CMS and see how it will help you stand out.

Direct Link to ArticlePermalink

The post Headless CMS: The Developers’ Best Friend appeared first on CSS-Tricks.

The web can be anything we want it to be

Css Tricks - Wed, 06/06/2018 - 6:59am

I really enjoyed this chat between Bruce Lawson and Mustafa Kurtuldu where they talked about browser support and the health of the web. Bruce expands upon a lot of the thoughts in a post he wrote last year called World Wide Web, Not Wealthy Western Web where he writes:

...across the world, regardless of disposable income, regardless of hardware or network speed, people want to consume the same kinds of goods and services. And if your websites are made for the whole world, not just the wealthy Western world, then the next 4 billion people might consume the stuff that your organization makes.

Another highlight is where Bruce also mentions that, as web developers, we might think that we’ve all moved on from jQuery as a community, and yet there are still millions of websites that depend upon jQuery to function properly. It's an interesting anecdote and relevant to recent discussions about React making a run at being the next thing to replace jQuery:

I’m just gonna throw this bomb here:

React is the new jQuery

There you go.

— Sara Soueidan (@SaraSoueidan) May 24, 2018

However! The most interesting part of this particular discussion, for me at least, is where they talk about Flash and the impact it had on the design of CSS3 and HTML5. They both argue that despite Flash’s shortcomings and accessibility issues, it happened to show us all that the web can be much more than just a place to store some hypertext and that ultimately it can be anything we want it to be.

Direct Link to ArticlePermalink

The post The web can be anything we want it to be appeared first on CSS-Tricks.

Animate Images and Videos with curtains.js

Css Tricks - Wed, 06/06/2018 - 4:00am

While browsing the latest award-winning websites, you may notice a lot of fancy image distortion animations or neat 3D effects. Most of them are created with WebGL, an API allowing GPU-accelerated image processing effects and animations. They also tend to use libraries built on top of WebGL such as three.js or pixi.js. Both are very powerful tools to create respectively 2D and 3D scenes.

But, you should keep in mind that those libraries were not originally designed to create slideshows or animate DOM elements. There is a library designed just for that, though, and we’re going to cover how to use it here in this post.

WebGL, CSS Positioning, and Responsiveness

Say you’re working with a library like three.js or pixi.js and you want to use it to create interactions, like mouseover and scroll events on elements. You might run into trouble! How do you position your WebGL elements relative to the document and other DOM elements? How would handle responsiveness?

This is exactly what I had in mind when creating curtains.js.

Curatins.js allows you to create planes containing images and videos (in WebGL we will call them textures) that act like plain HTML elements, with position and size defined by CSS rules. But these planes can be enhanced with the endless possibilities of WebGL and shaders.

Wait, shaders?

Shaders are small programs written in GLSL that will tell your GPU how to render your planes. Knowing how shaders work is mandatory here because this is how we will handle animations. If you’ve never heard of them, you may want to learn the basics first. There are plenty of good websites to start learning them, like The Book of Shaders.

Now that you get the idea, let’s create our first plane!

Setup of a basic plane

To display our first plane, we will need a bit of HTML, CSS, and some JavaScript to create the plane. Then our shaders will animate it.

HTML

The HTML will be really simple here. We will create a <div> that will hold our canvas, and a div that will hold our image.

<body> <!-- div that will hold our WebGL canvas --> <div id="canvas"></div> <!-- div used to create our plane --> <div class="plane"> <!-- image that will be used as a texture by our plane --> <img src="path/to/my-image.jpg" /> </div> </body> CSS

We will will use CSS to make sure the <div> that wraps the canvas will be bigger than our plane, and apply any size to the plane div. (Our WebGL plane will have the exact same size and positions of this div.)

body { /* make the body fit our viewport */ position: relative; width: 100%; height: 100vh; margin: 0; /* hide scrollbars */ overflow: hidden; } #canvas { /* make the canvas wrapper fit the document */ position: absolute; top: 0; right: 0; bottom: 0; left: 0; } .plane { /* define the size of your plane */ width: 80%; max-width: 1400px; height: 80vh; position: relative; top: 10vh; margin: 0 auto; } .plane img { /* hide the img element */ display: none; } JavaScript

There's a bit more work in the JavaScript. We need to instantiate our WebGL context, create a plane with uniform parameters, and use it.

window.onload = function() { // pass the id of the div that will wrap the canvas to set up our WebGL context and append the canvas to our wrapper var webGLCurtain = new Curtains("canvas"); // get our plane element var planeElement = document.getElementsByClassName("plane")[0]; // set our initial parameters (basic uniforms) var params = { vertexShaderID: "plane-vs", // our vertex shader ID fragmentShaderID: "plane-fs", // our framgent shader ID uniforms: { time: { name: "uTime", // uniform name that will be passed to our shaders type: "1f", // this means our uniform is a float value: 0, }, } } // create our plane mesh var plane = webGLCurtain.addPlane(planeElement, params); // use the onRender method of our plane fired at each requestAnimationFrame call plane.onRender(function() { plane.uniforms.time.value++; // update our time uniform value }); } Shaders

We need to write the vertex shader. It won’t be doing much except position our plane based on the model view and projection matrix and pass varyings to the fragment shader:

<!-- vertex shader --> <script id="plane-vs" type="x-shader/x-vertex"> #ifdef GL_ES precision mediump float; #endif // those are the mandatory attributes that the lib sets attribute vec3 aVertexPosition; attribute vec2 aTextureCoord; // those are mandatory uniforms that the lib sets and that contain our model view and projection matrix uniform mat4 uMVMatrix; uniform mat4 uPMatrix; // if you want to pass your vertex and texture coords to the fragment shader varying vec3 vVertexPosition; varying vec2 vTextureCoord; void main() { // get the vertex position from its attribute vec3 vertexPosition = aVertexPosition; // set its position based on projection and model view matrix gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0); // set the varyings vTextureCoord = aTextureCoord; vVertexPosition = vertexPosition; } </script>

Now our fragment shader. This is where we will add a little displacement effect based on our time uniform and the texture coordinates.

<!-- fragment shader --> <script id="plane-fs" type="x-shader/x-fragment"> #ifdef GL_ES precision mediump float; #endif // get our varyings varying vec3 vVertexPosition; varying vec2 vTextureCoord; // the uniform we declared inside our javascript uniform float uTime; // our texture sampler (this is the lib default name, but it could be changed) uniform sampler2D uSampler0; void main() { // get our texture coords vec2 textureCoord = vTextureCoord; // displace our pixels along both axis based on our time uniform and texture UVs // this will create a kind of water surface effect // try to comment a line or change the constants to see how it changes the effect // reminder : textures coords are ranging from 0.0 to 1.0 on both axis const float PI = 3.141592; textureCoord.x += ( sin(textureCoord.x * 10.0 + ((uTime * (PI / 3.0)) * 0.031)) + sin(textureCoord.y * 10.0 + ((uTime * (PI / 2.489)) * 0.017)) ) * 0.0075; textureCoord.y += ( sin(textureCoord.y * 20.0 + ((uTime * (PI / 2.023)) * 0.023)) + sin(textureCoord.x * 20.0 + ((uTime * (PI / 3.1254)) * 0.037)) ) * 0.0125; gl_FragColor = texture2D(uSampler0, textureCoord); } </script>

Et voilà! You’re all done, and if everything went well, you should be seeing something like this.

See the Pen curtains.js basic plane by Martin Laxenaire (@martinlaxenaire) on CodePen.

Adding 3D and interactions

Alright, that’s pretty cool so far, but we started this post talking about 3D and interactions, so let’s look at how we could add those in.

About vertices

To add a 3D effect we would have to change the plane vertices position inside the vertex shader. However in our first example, we did not specify how many vertices our plane should have, so it was created with a default geometry containing six vertices forming two triangles :

In order to get decent 3D animations, we would need more triangles, thus more vertices:

This plane has five segments along its width and five segments along its height. As a result, we have 50 triangles and 150 total vertices. Refactoring our JavaScript

Fortunately, it is easy to specify our plane definition as it could be set inside our initial parameters.

We are also going to listen to mouse position to add a bit of interaction. To do it properly, we will have to wait for the plane to be ready, convert our mouse document coordinates to our WebGL clip space coordinates and send them to the shaders as a uniform.

// we are using window onload event here but this is not mandatory window.onload = function() { // track the mouse positions to send it to the shaders var mousePosition = { x: 0, y: 0, }; // pass the id of the div that will wrap the canvas to set up our WebGL context and append the canvas to our wrapper var webGLCurtain = new Curtains("canvas"); // get our plane element var planeElement = document.getElementsByClassName("plane")[0]; // set our initial parameters (basic uniforms) var params = { vertexShaderID: "plane-vs", // our vertex shader ID fragmentShaderID: "plane-fs", // our framgent shader ID widthSegments: 20, heightSegments: 20, // we now have 20*20*6 = 2400 vertices ! uniforms: { time: { name: "uTime", // uniform name that will be passed to our shaders type: "1f", // this means our uniform is a float value: 0, }, mousePosition: { // our mouse position name: "uMousePosition", type: "2f", // notice this is a length 2 array of floats value: [mousePosition.x, mousePosition.y], }, mouseStrength: { // the strength of the effect (we will attenuate it if the mouse stops moving) name: "uMouseStrength", // uniform name that will be passed to our shaders type: "1f", // this means our uniform is a float value: 0, }, } } // create our plane mesh var plane = webGLCurtain.addPlane(planeElement, params); // once our plane is ready, we could start listening to mouse/touch events and update its uniforms plane.onReady(function() { // set a field of view of 35 to exagerate perspective // we could have done it directly in the initial params plane.setPerspective(35); // listen our mouse/touch events on the whole document // we will pass the plane as second argument of our function // we could be handling multiple planes that way document.body.addEventListener("mousemove", function(e) { handleMovement(e, plane); }); document.body.addEventListener("touchmove", function(e) { handleMovement(e, plane); }); }).onRender(function() { // update our time uniform value plane.uniforms.time.value++; // continually decrease mouse strength plane.uniforms.mouseStrength.value = Math.max(0, plane.uniforms.mouseStrength.value - 0.0075); }); // handle the mouse move event function handleMovement(e, plane) { // touch event if(e.targetTouches) { mousePosition.x = e.targetTouches[0].clientX; mousePosition.y = e.targetTouches[0].clientY; } // mouse event else { mousePosition.x = e.clientX; mousePosition.y = e.clientY; } // convert our mouse/touch position to coordinates relative to the vertices of the plane var mouseCoords = plane.mouseToPlaneCoords(mousePosition.x, mousePosition.y); // update our mouse position uniform plane.uniforms.mousePosition.value = [mouseCoords.x, mouseCoords.y]; // reassign mouse strength plane.uniforms.mouseStrength.value = 1; } }

Now that our JavaScript is done, we have to rewrite our shaders so that they’ll use our mouse position uniform.

Refactoring the shaders

Let’s look at our vertex shader first. We have three uniforms that we could use for our effect:

  1. the time which is constantly increasing
  2. the mouse position
  3. our mouse strength, which is constantly decreasing until the next mouse move

We will use all three of them to create a kind of 3D ripple effect.

<script id="plane-vs" type="x-shader/x-vertex"> #ifdef GL_ES precision mediump float; #endif // those are the mandatory attributes that the lib sets attribute vec3 aVertexPosition; attribute vec2 aTextureCoord; // those are mandatory uniforms that the lib sets and that contain our model view and projection matrix uniform mat4 uMVMatrix; uniform mat4 uPMatrix; // our time uniform uniform float uTime; // our mouse position uniform uniform vec2 uMousePosition; // our mouse strength uniform float uMouseStrength; // if you want to pass your vertex and texture coords to the fragment shader varying vec3 vVertexPosition; varying vec2 vTextureCoord; void main() { vec3 vertexPosition = aVertexPosition; // get the distance between our vertex and the mouse position float distanceFromMouse = distance(uMousePosition, vec2(vertexPosition.x, vertexPosition.y)); // this will define how close the ripples will be from each other. The bigger the number, the more ripples you'll get float rippleFactor = 6.0; // calculate our ripple effect float rippleEffect = cos(rippleFactor * (distanceFromMouse - (uTime / 120.0))); // calculate our distortion effect float distortionEffect = rippleEffect * uMouseStrength; // apply it to our vertex position vertexPosition += distortionEffect / 15.0; gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0); // varyings vTextureCoord = aTextureCoord; vVertexPosition = vertexPosition; } </script>

As for the fragment shader, we are going to keep it simple. We are going to fake lights and shadows based on each vertex position:

<script id="plane-fs" type="x-shader/x-fragment"> #ifdef GL_ES precision mediump float; #endif // get our varyings varying vec3 vVertexPosition; varying vec2 vTextureCoord; // our texture sampler (this is the lib default name, but it could be changed) uniform sampler2D uSampler0; void main() { // get our texture coords vec2 textureCoords = vTextureCoord; // apply our texture vec4 finalColor = texture2D(uSampler0, textureCoords); // fake shadows based on vertex position along Z axis finalColor.rgb -= clamp(-vVertexPosition.z, 0.0, 1.0); // fake lights based on vertex position along Z axis finalColor.rgb += clamp(vVertexPosition.z, 0.0, 1.0); // handling premultiplied alpha (useful if we were using a png with transparency) finalColor = vec4(finalColor.rgb * finalColor.a, finalColor.a); gl_FragColor = finalColor; } </script>

And there you go!

See the Pen curtains.js ripple effect example by Martin Laxenaire (@martinlaxenaire) on CodePen.

With these two simple examples, we’ve seen how to create a plane and interact with it.

Videos and displacement shaders

Our last example will create a basic fullscreen video slideshow using a displacement shader to enhance the transitions.

Displacement shader concept

The displacement shader will create a nice distortion effect. It will be written inside our fragment shader using a grayscale picture and will offset the pixel coordinates of the videos based on the texture RGB values. Here’s the image we will be using:

The effect will be calculated based on each pixel RGB value, with a black pixel being [0, 0, 0] and a white pixel [1, 1, 1] (GLSL equivalent for [255, 255, 255]). To simplify, we will use only the red channel value, as with a grayscale image red, green and blue are always equal.

You can try to create your own grayscale image (it works great with geometric shape ) to get your unique transition effect.

Multiple textures and videos

A plane can have more than one texture simply by adding multiple image tags. This time, instead of images we want to use videos. We just have to replace the <img /> tags with a <video /> one. However there are two things to know when it comes to video:

  • The video will always fit the exact size of the plane, which means your plane has to have the same width/height ratio as your video. This is not a big deal tho because it is easy to handle with CSS.
  • On mobile devices, we can’t autoplay videos without a user gesture, like a click event. It is therefore safer to add a "enter site" button to display and launch our videos.
HTML

The HTML is still pretty straightforward. We will create our canvas div wrapper, our plane div containing the textures and a button to trigger the video autoplay. Just notice the use of the data-sampler attribute on the image and video tags—it will be useful inside our fragment shader.

<body> <div id="canvas"></div> <!-- this div will handle the fullscreen video sizes and positions --> <div class="plane-wrapper"> <div class="plane"> <!-- notice here we are using the data-sampler attribute to name our sampler uniforms --> <img src="path/to/displacement.jpg" data-sampler="displacement" /> <video src="path/to/video.mp4" data-sampler="firstTexture"></video> <video src="path/to/video-2.mp4" data-sampler="secondTexture"></video> </div> </div> <div id="enter-site-wrapper"> <span id="enter-site"> Click to enter site </span> </div> </body> CSS

The stylesheet will handle a few things: display the button and hide the canvas before the user has entered the site, size and position our plane-wrapper div to handle fullscreen responsive videos.

@media screen { body { margin: 0; font-size: 18px; font-family: 'PT Sans', Verdana, sans-serif; background: #212121; line-height: 1.4; height: 100vh; width: 100vw; overflow: hidden; } /*** canvas ***/ #canvas { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 10; /* hide the canvas until the user clicks the button */ opacity: 0; transition: opacity 0.5s ease-in; } /* display the canvas */ .video-started #canvas { opacity: 1; } .plane-wrapper { position: absolute; /* center our plane wrapper */ left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 15; } .plane { position: absolute; top: 0; right: 0; bottom: 0; left: 0; /* tell the user he can click the plane */ cursor: pointer; } /* hide the original image and videos */ .plane img, .plane video { display: none; } /* center the button */ #enter-site-wrapper { display: flex; justify-content: center; align-items: center; align-content: center; position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 30; /* hide the button until everything is ready */ opacity: 0; transition: opacity 0.5s ease-in; } /* show the button */ .curtains-ready #enter-site-wrapper { opacity: 1; } /* hide the button after the click event */ .curtains-ready.video-started #enter-site-wrapper { opacity: 0; pointer-events: none; } #enter-site { padding: 20px; color: white; background: #ee6557; max-width: 200px; text-align: center; cursor: pointer; } } /* fullscreen video responsive */ @media screen and (max-aspect-ratio: 1920/1080) { .plane-wrapper { height: 100vh; width: 177vh; } } @media screen and (min-aspect-ratio: 1920/1080) { .plane-wrapper { width: 100vw; height: 56.25vw; } } JavaScript

As for the JavaScript, we will go like this:

  • Set a couple variables to store our slideshow state
  • Create the Curtains object and add the plane to it
  • When the plane is ready, listen to a click event to start our videos playback (notice the use of the playVideos() method). Add another click event to switch between the two videos.
  • Update our transition timer uniform inside the onRender() method
window.onload = function() { // here we will handle which texture is visible and the timer to transition between images var activeTexture = 1; var transitionTimer = 0; // set up our WebGL context and append the canvas to our wrapper var webGLCurtain = new Curtains("canvas"); // get our plane element var planeElements = document.getElementsByClassName("plane"); // some basic parameters var params = { vertexShaderID: "plane-vs", fragmentShaderID: "plane-fs", imageCover: false, // our displacement texture has to fit the plane uniforms: { transitionTimer: { name: "uTransitionTimer", type: "1f", value: 0, }, }, } var plane = webGLCurtain.addPlane(planeElements[0], params); // create our plane plane.onReady(function() { // display the button document.body.classList.add("curtains-ready"); // when our plane is ready we add a click event listener that will switch the active texture value planeElements[0].addEventListener("click", function() { if(activeTexture == 1) { activeTexture = 2; } else { activeTexture = 1; } }); // click to play the videos document.getElementById("enter-site").addEventListener("click", function() { // display canvas and hide the button document.body.classList.add("video-started"); // play our videos plane.playVideos(); }, false); }).onRender(function() { // increase or decrease our timer based on the active texture value // at 60fps this should last one second if(activeTexture == 2) { transitionTimer = Math.min(60, transitionTimer + 1); } else { transitionTimer = Math.max(0, transitionTimer - 1); } // update our transition timer uniform plane.uniforms.transitionTimer.value = transitionTimer; }); } Shaders

This is where all the magic will occur. Like in our first example, the vertex shader won’t do much and you’ll have to focus on the fragment shader that will create a “dive in" effect:

<script id="plane-vs" type="x-shader/x-vertex"> #ifdef GL_ES precision mediump float; #endif // default mandatory variables attribute vec3 aVertexPosition; attribute vec2 aTextureCoord; uniform mat4 uMVMatrix; uniform mat4 uPMatrix; // varyings varying vec3 vVertexPosition; varying vec2 vTextureCoord; // custom uniforms uniform float uTransitionTimer; void main() { vec3 vertexPosition = aVertexPosition; gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0); // varyings vTextureCoord = aTextureCoord; vVertexPosition = vertexPosition; } </script> <script id="plane-fs" type="x-shader/x-fragment"> #ifdef GL_ES precision mediump float; #endif varying vec3 vVertexPosition; varying vec2 vTextureCoord; // custom uniforms uniform float uTransitionTimer; // our textures samplers // notice how it matches our data-sampler attributes uniform sampler2D firstTexture; uniform sampler2D secondTexture; uniform sampler2D displacement; void main( void ) { // our texture coords vec2 textureCoords = vec2(vTextureCoord.x, vTextureCoord.y); // our displacement texture vec4 displacementTexture = texture2D(displacement, textureCoords); // our displacement factor is a float varying from 1 to 0 based on the timer float displacementFactor = 1.0 - (cos(uTransitionTimer / (60.0 / 3.141592)) + 1.0) / 2.0; // the effect factor will tell which way we want to displace our pixels // the farther from the center of the videos, the stronger it will be vec2 effectFactor = vec2((textureCoords.x - 0.5) * 0.75, (textureCoords.y - 0.5) * 0.75); // calculate our displaced coordinates to our first video vec2 firstDisplacementCoords = vec2(textureCoords.x - displacementFactor * (displacementTexture.r * effectFactor.x), textureCoords.y - displacementFactor * (displacementTexture.r * effectFactor.y)); // opposite displacement effect on the second video vec2 secondDisplacementCoords = vec2(textureCoords.x - (1.0 - displacementFactor) * (displacementTexture.r * effectFactor.x), textureCoords.y - (1.0 - displacementFactor) * (displacementTexture.r * effectFactor.y)); // apply the textures vec4 firstDistortedColor = texture2D(firstTexture, firstDisplacementCoords); vec4 secondDistortedColor = texture2D(secondTexture, secondDisplacementCoords); // blend both textures based on our displacement factor vec4 finalColor = mix(firstDistortedColor, secondDistortedColor, displacementFactor); // handling premultiplied alpha finalColor = vec4(finalColor.rgb * finalColor.a, finalColor.a); // apply our shader gl_FragColor = finalColor; } </script>

Here’s our little video slideshow with a cool transition effect:

See the Pen curtains.js video slideshow by Martin Laxenaire (@martinlaxenaire) on CodePen.

This example is a great way to show you how to create a slideshow with curtains.js: you might want to use images instead of videos, change the displacement texture, modify the fragment shader or even add more slides…

Going deeper

We’ve just scraped the surface of what’s possible with curtains.js. You could try to create multiple planes with a cool mouse over effect for your article thumbs for example. The possibilities are almost endless.

If you want to see more examples covering all those basics usages, you can check the library website or the GitHub repo.

The post Animate Images and Videos with curtains.js appeared first on CSS-Tricks.

1 Element CSS Rainbow Gradient Infinity

Css Tricks - Tue, 06/05/2018 - 4:04am

I first got the idea to CSS something of the kind when I saw this gradient infinity logo by Infographic Paradise:

The original gradient infinity.

After four hours and some twenty minutes, of which over four hours were spent on tweaking positioning, edges and highlights... I finally had the result below:

My version of the rainbow gradient infinity.

The gradient doesn't look like in the original illustration, as I chose to generate the rainbow logically instead of using the Dev Tools picker or something like that, but other than that, I think I got pretty close—let's see how I did that!

Markup

As you've probably already guessed from the title, the HTML is just one element:

<div class='?'></div> Styling Deciding on the approach

The first idea that might come to mind when seeing the above would be using conic gradients as border images. Unfortunately, border-image and border-radius don't play well together, as illustrated by the interactive demo below:

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

Whenever we set a border-image, border-radius just gets ignored, so using the two together is sadly not an option.

So the approach we take here is using conic-gradient() backgrounds and then getting rid of the part in the middle with the help of a mask. Let's see how that works!

Creating the two ? halves

We first decide on an outer diameter.

$do: 12.5em;

We create the two halves of the infinity symbol using the ::before and ::after pseudo-elements of our .? element. In order to place these two pseudo-elements next to one another, we use a flex layout on their parent (the infinity element .?). Each of these has both the width and the height equal to the outer diameter $do. We also round them with a border-radius of 50% and we give them a dummy background so we can see them.

.? { display: flex; &:before, &:after { width: $do; height: $do; border-radius: 50%; background: #000; content: ''; } }

We've also placed the .? element in the middle of its parent (the body in this case) both vertically and horizontally by using the flexbox approach.

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

How conic-gradient() works

In order to create the conic-gradient() backgrounds for the two haves, we must first understand how the conic-gradient() function works.

If inside the conic-gradient() function we have a list of stops without explicit positions, then the first is taken to be at 0% (or 0deg, same thing), the last is taken to be at 100% (or 360deg), while all those left are distributed evenly in the [0%, 100%] interval.

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

If we have just 2 stops, it's simple. The first is at 0%, the second (and last) at 100% and there are no other stops in between.

If we have 3 stops, the first is at 0%, the last (third) at 100%, while the second is dead in the middle of the [0%, 100%] interval, at 50%.

If we have 4 stops, the first is at 0%, the last (fourth) at 100%, while the second and third split the [0%, 100%] interval into 3 equal intervals, being positioned at 33.(3)% and 66.(6)% respectively.

If we have 5 stops, the first is at 0%, the last (fifth) at 100%, while the second, third and fourth split the [0%, 100%] interval into 4 equal intervals being positioned at 25%, 50% and 75% respectively.

If we have 6 stops, the first is at 0%, the last (sixth) at 100%, while the second, third, fourth and fifth split the [0%, 100%] interval into 5 equal intervals being positioned at 20%, 40%, 60% and 80% respectively.

In general, if we have n stops, the first is at 0%, the last at 100%, while the ones in between split the [0%, 100%] interval into n-1 eqial intervals spanning 100%/(n-1) each. If we give the stops 0-based indices, then each one of them is positioned at i*100%/(n-1).

For the first one, i is 0, which gives us 0*100%/(n-1) = 0%.

For the last (n-th) one, i is n-1, which gives us (n-1)*100%/(n-1) = 100%.

Here, we choose to use 9 stops which means we split the [0%, 100%] interval into 8 equal intervals.

Alright, but how do we get the stop list?

The hsl() stops

Well, for simplicity, we choose to generate it as a list of HSL values. We keep the saturation and the lightness fixed and we vary the hue. The hue is an angle value that goes from 0 to 360, as we can see here:

Visual representation of the hue scale from 0 to 360 (saturation and lightness being kept constant).

With this in mind, we can construct a list of hsl() stops with fixed saturation and lightness and varying hue if we know the start hue $hue-start, the hue range $hue-range (this is the end hue minus the start hue) and the number of stops $num-stops.

Let's say we keep the saturation and the lightness fixed at 85% and 57%, respectively (arbitrary values that can probably be tweaked for better results) and, for example, we might go from a start hue of 240 to an end hue of 300 and use 4 stops.

In order to generate this list of stops, we use a get-stops() function that takes these three things as arguments:

@function get-stops($hue-start, $hue-range, $num-stops) {}

We create the list of stops $list which is originally empty (and which we'll return at the end after we populate it). We also compute the span of one of the equal intervals our stops split the full start to end interval into ($unit).

@function get-stops($hue-start, $hue-range, $num-stops) { $list: (); $unit: $hue-range/($num-stops - 1); /* populate the list of stops $list */ @return $list }

In order to populate our $list, we loop through the stops, compute the current hue, use the current hue to generate the hsl() value at that stop and then then add it to the list of stops:

@for $i from 0 to $num-stops { $hue-curr: $hue-start + $i*$unit; $list: $list, hsl($hue-curr, 85%, 57%); }

We can now use the stop list this function returns for any kind of gradient, as it can be seen from the usage examples for this function shown in the interactive demo below (navigation works both by using the previous/next buttons on the sides as well as the arrow keys and the PgDn/ PgUp keys):

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

Note how, when our range passes one end of the [0, 360] interval, it continues from the other end. For example, when the start hue is 30 and the range is -210 (the fourth example), we can only go down to 0, so then we continue going down from 360.

Conic gradients for our two halves

Alright, but how do we determine the $hue-start and the $hue-range for our particular case?

In the original image, we draw a line in between the central points of the two halves of the loop and, starting from this line, going clockwise in both cases, we see where we start from and where we end up in the [0, 360] hue interval and what other hues we pass through.

We start from the line connecting the central points of the two halves and we go around them in the clockwise direction.

To simplify things, we consider we pass through the whole [0, 360] hue scale going along our infinity symbol. This means the range for each half is 180 (half of 360) in absolute value.

Keywords to hue values correspondence for saturation and lightness fixed at 100% and 50% respectively.

On the left half, we start from something that looks like it's in between some kind of cyan (hue 180) and some kind of lime (hue 120), so we take the start hue to be the average of the hues of these two (180 + 120)/2 = 150.

The plan for the left half.

We get to some kind of red, which is 180 away from the start value, so at 330, whether we subtract or add 180:

(150 - 180 + 360)%360 = (150 + 180 + 360)%360 = 330

So... do we go up or down? Well, we pass through yellows which are around 60 on the hue scale, so that's going down from 150, not up. Going down means our range is negative (-180).

The plan for the right half.

On the right half, we also start from the same hue in between cyan and lime (150) and we also end at the same kind of red (330), but this time we pass through blues, which are around 240, meaning we go up from our start hue of 150, so our range is positive in this case (180).

As far as the number of stops goes, 9 should suffice.

Now update our code using the values for the left half as the defaults for our function:

@function get-stops($hue-start: 150, $hue-range: -180, $num-stops: 9) { /* same as before */ } .? { display: flex; &:before, &:after { /* same as before */ background: conic-gradient(get-stops()); } &:after { background: conic-gradient(get-stops(150, 180)); } }

And now our two discs have conic-gradient() backgrounds:

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

However, we don't want these conic gradients to start from the top.

For the first disc, we want it to start from the right—that's at 90° from the top in the clockwise (positive) direction. For the second disc, we want it to start from the left—that's at 90° from the top in the other (negative) direction, which is equivalent to 270° from the top in the clockwise direction (because negative angles don't appear to work from some reason).

Angular offsets from the top for our two halves.

Let's modify our code to achieve this:

.? { display: flex; &:before, &:after { /* same as before */ background: conic-gradient(from 90deg, get-stops()); } &:after { background: conic-gradient(from 270deg, get-stops(150, 180)); } }

So far, so good!

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

From &#x1f967; to &#x1f369;

The next step is to cut holes out of our two halves. We do this with a mask or, more precisely, with a radial-gradient() one. This cuts out Edge support for now, but since it's something that's in development, it's probably going to be a cross-browser solution at some point in the not too far future.

Remember that CSS gradient masks are alpha masks by default (and only Firefox currently allows changing this via mask-mode), meaning that only the alpha channel matters. Overlaying the mask over our element makes every pixel of this element use the alpha channel of the corresponding pixel of the mask. If the mask pixel is completely transparent (its alpha value is 0), then so will the corresponding pixel of the element.

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

In order to create the mask, we compute the outer radius $ro (half the outer diameter $do) and the inner radius $ri (a fraction of the outer radius $ro).

$ro: .5*$do; $ri: .52*$ro; $m: radial-gradient(transparent $ri, red 0);

We then set the mask on our two halves:

.? { /* same as before */ &:before, &:after { /* same as before */ mask: $m; } }

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

This looks perfect in Firefox, but the edges of radial gradients with abrupt transitions from one stop to another look ugly in Chrome and, consequently, so do the inner edges of our rings.

Close-up of the inner edge of the right half in Chrome.

The fix here would be not to have an abrupt transition between stops, but spread it out over a small distance, let's say half a pixel:

$m: radial-gradient(transparent calc(#{$ri} - .5px), red $ri);

We now got rid of the jagged edges in Chrome:

Close-up of the inner edge of the right half in Chrome after spreading out the transition between stops over half a pixel.

The following step is to offset the two halves such that they actually form an infinity symbol. The visible circular strips both have the same width, the difference between the outer radius $ro and the inner radius $ri. This means we need to shift each laterally by half this difference $ri - $ri.

.? { /* same as before */ &:before, &:after { /* same as before */ margin: 0 (-.5*($ro - $ri)); } }

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

Intersecting halves

We're getting closer, but we still have a very big problem here. We don't want the right part of the loop to be completely over the left one. Instead, we want the top half of the right part to be over that of the left part and the bottom half of the left part to be over that of the right part.

So how do we achieve that?

We take a similar approach to that presented in an older article: using 3D!

In order to better understand how this works, consider the two card example below. When we rotate them around their x axes, they're not in the plane of the screen anymore. A positive rotation brings the bottom forward and pushes the top back. A negative rotation brings the top forward and pushes the bottom back.

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

Note that the demo above doesn't work in Edge.

So if we give the left one a positive rotation and the right one a negative rotation, then the top half of the right one appears in front of the top half of the left one and the other way around for the bottom halves.

Addiing perspective makes what's closer to our eyes appears bigger and what's further away appears smaller and we use way smaller angles. Without it, we have the 3D plane intersection without the 3D appearance.

Note that both our halves need to be in the same 3D context, something that's achieved by setting transform-style: preserve-3d on the .? element.

.? { /* same as before */ transform-style: preserve-3d; &:before, &:after { /* same as before */ transform: rotatex(1deg); } &:after { /* same as before */ transform: rotatex(-1deg); } }

And now we're almost there, but not quite:

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

Fine tuning

We have a little reddish strip in the middle because the gradient ends and the intersection line don't quite match:

Close-up of small issue at the intersection of the two halves.

A pretty ugly, but efficient fix is to add a 1px translation before the rotation on the right part (the ::after pseudo-element):

.?:after { transform: translate(1px) rotatex(-1deg) }

Much better!

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

This still isn't perfect though. Since the inner edges of our two rings are a bit blurry, the transition in between them and the crisp outer ones looks a bit odd, so maybe we can do better there:

Close-up of continuity issue (crisp outer edges meeting blurry inner ones).

A quick fix here would be to add a radial-gradient() cover on each of the two halves. This cover is transparent white for most of the unmasked part of the two halves and goes to solid white along both their inner and outer edges such that we have nice continuity:

$gc: radial-gradient(#fff $ri, rgba(#fff, 0) calc(#{$ri} + 1px), rgba(#fff, 0) calc(#{$ro} - 1px), #fff calc(#{$ro} - .5px)); .? { /* same as before */ &:before, &:after { /* same as before */ background: $gc, conic-gradient(from 90deg, get-stops()); } &:after { /* same as before */ background: $gc, conic-gradient(from 270deg, get-stops(150, 180)); } }

The benefit becomes more obvious once we add a dark background to the body:

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

Now it looks better even when zooming in:

No more sharp contrast between inner and outer edges. The final result

Finally, we add some prettifying touches by layering some more subtle radial gradient highlights over the two halves. This was the part that took me the most because it involved the least amount of logic and the most amount of trial and error. At this point, I just layered the original image underneath the .? element, made the two halves semi-transparent and started adding gradients and tweaking them until they pretty much matched the highlights. And you can see when I got sick of it because that's when the position values become rougher approximations with fewer decimals.

Another cool touch would be drop shadows on the whole thing using a filter on the body. Sadly, this breaks the 3D intersection effect in Firefox, which means we cannot add it there, too.

@supports not (-moz-transform: scale(2)) { filter: drop-shadow(.25em .25em .25em #000) drop-shadow(.25em .25em .5em #000); }

We now have the final static result!

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

Spicing it up with animation!

When I first shared this demo, I got asked about animating it. I initially thought this would be complicated, but then it hit me that, thanks to Houdini, it doesn't have to be!

As mentioned in my previous article, we can animate in between stops, let's say from a red to a blue. In our case, the saturation and lightness components of the hsl() values used to generate the rainbow gradient stay constant, all that changes is the hue.

For each and every stop, the hue goes from its initial value to its initial value plus 360, thus passing through the whole hue scale in the process. This is equivalent to keeping the initial hue constant and varying an offset. This offset --off is the custom property we animate.

Sadly, this means support is limited to Blink browsers with the Experimental Web Platform features flag enabled.

The Experimental Web Platform features flag enabled in Chrome.

Still, let's see how we put it all into code!

For starters, we modify the get-stops() function such that the current hue at any time is the initial hue of the current stop $hue-curr plus our offset --off:

$list: $list, hsl(calc(#{$hue-curr} + var(--off, 0)), 85%, 57%);

Next, we register this custom property:

CSS.registerProperty({ name: '--off', syntax: '<number>', initialValue: 0; })

And finally, we animate it to 360:

.? { /* same as before */ &:before, &:after { /* same as before */ animation: shift 2s linear infinite; } } @keyframes shift { to { --off: 360 } }

This gives us our animated gradient infinity!

Animated ? logo (live demo, Blink only with flag enabled).

That's it! I hope you've enjoyed this dive into what can be done with CSS these days!

The post 1 Element CSS Rainbow Gradient Infinity appeared first on CSS-Tricks.

performance.now() conference,8/9 November, Amsterdam

QuirksBlog - Tue, 06/05/2018 - 12:23am

It’s always a nail-bitingly tense few months after you decide to create a new conference hoping it’s going to be a success. We decided to inflict this tension on ourselves once again with our latest venture: performance.now().

Conceived during a dinner with Tim Kadlec over a year ago, nurtured by Tim’s good graces as well as Steve Souders’s, its every whim served by Krijn and me, performance.now() is a two-day one-track front-end performance conference in Amsterdam on 8th and 9th of November.

(And no, we don’t have the obvious domain name. We’d love to get it, but .now domain sales haven’t started yet.)

Our speakers will treat topics including JavaScript, CSS, PWAs & AMP, responsive design, image optimization, performance budgets, frameworks, monitoring, browsers, mobile devices, custom fonts, and perceived performance.

Who are our speakers? Glad you asked. Harry Roberts, Tammy Everts, Anna Migas, and Zach Leatherman. Not convinced? Andrew Betts, Paul Irish, and Natasha Rooney. Need more? Yoav Weiss and Katie Sylor-Miller. And of course Steve Souders and Tim Kadlec — why let them go to waste when they’re involved anyway? And there are five more speakers as yet to be announced. The speaker page will be updated as soon as we’ve got something to share.

We would not be able to put on this show without support from our sponsors, currently SpeedCurve, Catchpoint, Filament Group, and Varnish Software. We hope to be able to announce more sponsors shortly.

Tickets are €550 + VAT (early birds have already sold out). Lunch and post-conference drinks included. Nice weather not included; this is November in Amsterdam, after all. But we’ll survive.

Will we see you at performance.now()?

Cataloging Interfaces: Values To Great UX Design

Usability Geek - Mon, 06/04/2018 - 11:41am
The sheer amount of digital products being launched today is too substantial to count. Still, many of them fail along the way, never manage to generate profit or establish themselves in the consensus...
Categories: Web Standards

Combining the Powers of SEM and BIO for Improving CSS

Css Tricks - Mon, 06/04/2018 - 4:07am

CSS is easy, some might argue, but that "easiness" can cause messy code. This is especially true through power of preprocessors like Sass or Less where, if you aren’t careful, your CSS can become harder to deal with instead of easier. Sass? Harder? This Gist shows a great example of Sass nesting hell.

If your Sass code looks like that, you can definitely improve your code with SEM & BIO, a CSS technique I’ll introduce you to now!

In this article, I am going to use the code example below to explain how SEM and BIO works and how they can help enhance your CSS strategy.

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

Generally, SEM is concerned with high level CSS philosophy whereas BIO is an actual technique to help you write better CSS to achieve SEM. The main purpose of both SEM and BIO is to better handle the CSS specificity which is one of the most important concepts you should understand for CSS.

Credit: Nana Jeon

First of all, let’s talk about SEM.

SEM

SEM stands for:

  • Scalable
  • Extensible
  • Maintainable

Trying to achieve those three factors can definitely improve your CSS code and it helps you build your components much more solid.

Let's talk more about each factor in detail.

Scalable

A scalable (or reusable) component means that the same looking components should be used wherever you want without making any coding changes.

Credit: Nana Jeon

From the CodePen example above, the "Search" button in the header looks exactly same as the ‘Link' button in the sidebar. When we compare the HTML markup,

  • the "Search" button is <button> element
  • but the "Link" button is <a role="button" ...> element

...and even if the markup is different, the styles are identical by using the same classes: .c-btn and .c-btn--yellow.

The button styles are scalable and it allows you to add the same looking components anywhere you want as it won’t be polluted by its parents or siblings. This can save you from the big headache of not knowing why totally unrelated components are broken even if the changes are made on a different component from a totally different place.

Source: Cartoon Network Extensible

An extensible component can easily provide additional features/functionalities without breaking itself or having to be written from scratch.

Let’s look at the CodePen example again.

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

The button in the header and in the main section look quite similar besides the 3D effect. In this case, instead of creating two different sets of buttons having totally different code bases, we could extend the plain button style by just adding the 3D effect to it.

It goes the same with the button in the footer. Even though the button has a different color and size, we could easily extend it by adding or removing new or different features.

Credit: Nana Jeon Maintainable

Probably one of the biggest challenges for most front-enders is to understand CSS written by other people, or our past selves. We sometimes spend more time trying to understand the existing code than adding awesomely-written new code.

The issue usually comes from:

  • no comments, whatsoever
  • over-engineering
  • no single source of truth
  • no coding standard/best practice considered
  • or all of them above
Credit: Nana Jeon

With SEM and BIO, we can definitely improve the code and save others (including ourselves!) from messy, unmaintainable code.

BIO

There are many great techniques out there to improve the way we write CSS, and from my experience, I found the following three techniques that make up the BIO acronym work very well together

  • BEM
  • ITCSS
  • OOCSS
Credit: Nana Jeon

A lot of developers/engineers already know those famous techniques but I would like to go through each of them and talk about the way I use those techniques.

BEM

BEM is very popular methodology and it has been helping us significantly improve the way we think about CSS and Sass/Less.

BEM stands for:

  • Block
  • Element
  • Modifier
Credit: Nana Jeon

As the bad example above shows, we tend to overuse the power of Sass/Less and falling into nesting-hell. But with BEM, we start to have a very low CSS specificity by maintaining the styles in one (or two) levels of nesting.

If you’ve experienced any battles fighting higher CSS specificity, you will know how painful it is to come out a winner.

Going back to our example, the HTML markup looks like this:

<div class="o-grid"> <div class="o-grid__item o-grid__header"> ... </div> <div class="o-grid__item o-grid__main"> ... </div> <div class="o-grid__item o-grid__sidebar"> ... </div> <div class="o-grid__item o-grid__footer"> ... </div> </div>

The example consists of:

  • a block: .o-grid
  • elements: .o-grid__item, .o-grid__header, .o-grid__main, .o-grid__sidebar, .o-grid__footer

Because BEM provides a naming convention that stresses unique classes, we don’t have to go into deep nesting like:

.o-grid { .o-grid__item { ... } }

Instead, we can define the styles it with fewer levels:

.o-grid__item { ... }

This is the biggest merit of BEM; lowering the CSS specificity which improves the whole CSS coding efficiency and experience.

One issue I still see occasionally even with BEM is poor naming. If you don’t pay enough attention, you can wind up with a really long class name like:

/* Yikes! */ .o-grid__item-search-button-text-svg-icon { ... }

When you create a class name, go with the core concept of BEM: your component is a block and all elements inside are individually attached to the block.

From our example again, I named .o-grid__form instead of .o-grid__item-form because the form itself is a separate component and doesn’t have to be tied with and be a child of o-grid__item.

Also, to more efficiently control styles, I have added another class name o-grid__header along with o-grid__item to extend the styles. Moreover, the button contains BEM-styled classes with the approach of OOCSS, which we’ll touch on next.

OOCSS

As we have already discussed, there are many great CSS methodologies and strategies helping us improve the way we write CSS. However, I see a lot of folks forcing themselves to decide on one methodology to use out of the bunch.

From my experience, combining methodologies can actually enhance their benefits by combining the best of multiple worlds. For example, I personally have found that BEM and OOCSS work very well together.

OOCSS stands for Object Oriented CSS and you can think of it working like Lego blocks:

Source: Flickr

OOCSS creates each individual part separately and then constructs them together in order to build components.

From our example, I have created buttons using the OOCSS naming convention:

  • .c-btn
  • .c-btn--yellow
  • .c-btn--blue
  • .c-btn--3d
  • .c-btn--large

To render the yellow search button in our example header, we combine these classes:

  • .c-btn
  • .c-btn--yellow

If we want to have the 3D button in the main section, we add in the 3D class, .c-btn--3d and call it a day.

And for the blue button in the footer, we can switch the yellow modifier to blue along with the large modifier. As you can see, the button is not depending on the header block giving us greater flexibility with how we use and repurpose components. And, by doing so, we can construct the buttons without impacting any other components or patterns while gaining the benefit of easily extending new presentational feature, like alternative colors and shapes.

Here example of a button collection created using OOCSS to create the variations:

See the Pen Modern Button Collection by Ryan Yu (@iamryanyu) on CodePen.

On top of BEM and OOCSS, with the help of ITCSS, we can further improve our CSS strategy. Let’s look at that method next.

ITCSS

ITCSS stands for Inverted Triangle CSS and it helps organize CSS by applying a structure that determines how specific to get with a specific component. Lubos Kmetko has written an excellent overview of ITCSS that is worth reading.

You can see how I have put ITCSS to use by splitting styles up by grouped levels of specificity in this Gist.

Based on that example, you can see how I named components by adding a namespace to the class. So, for example, a the "button" component is prefixed with a "c" (.c-button) to indicate the component status and prevent it from being mistaken for another item. By doing so, everyone working on the project knows the function of that specific class and how changing its properties might affect other areas.

Here’s a visual that illustrates all the ITCSS levels:

Let’s go through each section.

Settings

Settings are generally a collection of variables that do not generate CSS, but are applied to classes. Some examples include:

  • Base
  • Color
  • Typography
  • Animation
Tools

Tools also will not produce any CSS yet and are typically preprocessor functions that help write or extend properties on classes:

  • Functions
  • Placeholders
  • Mixins
  • Media queries
Vendors

Vendors are third-party styles that are being used on a project. Think of things like reset.css, normalize.css, or even Foundation and Bootstrap.

The reason these styles are higher up in the structure is so we can override them if needed. As you may recall, if the same class is called twice, the cascade will render the properties of the second instance, assuming the properties are exactly the same:

.btn--large { padding: 3em; } /* This one wins out */ .btn--large { padding: 5em; }

Just for the side note, in Sass, you can use ~ to point to the node_modules folder so you are able to import style assets from the source rather than having to move it into your own directories.

@import '~modern-normalize/modern-normalize'; Objects

Objects (namespace: o-) are used for design patterns, such as layouts, where items are being arranged rather than decorated. Object classes are used across all pages, so if you make any changes to the object classes, you should be very careful because any changes are going to affect each and every page throughout the website.

The most common object classes I use are:

  • .o-page: the most outer container which usually contains max-width: 100vw and overflow: hidden.
  • .o-main: the outer container for the main area.
  • .o-container: the outer container for components and it usually provides a fixed width.
  • .o-content: in case if any extra configuration is needed for the actual content area.
  • .o-grid: if a grid layout with different number of columns are required.

Do you use any other object classes? If so, please share with me. &#x1f603;

Elements

Elements (namespace: e-) are the HTML native elements which we would not style based on the class names. For example, we should provide default styles to <a> element rather than .link class.

// Do this for the default link style a { text-decoration: none; &:hover { background-color: blue; color: white; } } // Don’t provide the default link style to a class .link { text-decoration: none; &:hover { background-color: blue; color: white; } }

It is because, especially in a CMS like WordPress, you wouldn’t want to add a class every single time you want to use a link in content. Hence, we provide a default style to the <a> element so without any class, the link will still have good-looking styles.

Components

A component (namespace: c-) is a small feature that makes up a part of the website. Think buttons, accordions, sliders, modal dialogs, etc. Each component is fully functional by itself and does not rely on any other components. This fact should be considered when you name the component.

For example, the button in the main section from the example above shouldn’t be called .c-main-button because main scopes it inside the main section and limits the use of it in other places, like a sidebar. Something like .c-btn is much nicer because the button is no longer tied to any other specific sections of the page.

If you need any extra features, you can always extend properties with a BEM modifier (combining powers!) or use Scope, which is coming up in a bit.

Patterns

A lot of developers/engineers use the terms component and pattern synonymously and that’s totally fine if you are more comfortable with that. It is just my preference to separate those two terms.

For a general rule of thumb, I think of a pattern (namespace: p-) as a combination of components but in a way that is not scaleable.

For example, I would consider the accordion as a component. It is scaleable and reusable on its own, meaning that it can be used in other parts of the website without making any changes even if the accordion would contain other components such as buttons.

On the other hand, the header, for example, would be a pattern because it is not scaleable (the header cannot be used in the content or sidebar area) and also contains other components such as buttons, accordions, menus, logos, search form etc.

Scope

Be warned; I only use the scope if it’s absolutely necessary. The purpose of the scope (namespace: s-) is to give us the highest specificity so we can overwrite any styles for a specific purpose.

Remember, If you find yourself using the scope class many times, you might be writing styles that are too specific and you should consider refactor your CSS structure.

Here is a simple example of the use of the scope class, .s-home.

.c-accordion { .s-home & { // Changing the background color specically on the homepage background-color: tomato; } }

As a side note, the above example could actually be refactored by providing a modifier to the accordion (e.g., .c-accordion--bg-tomato) instead of using the scope class. That would be a much more extensible way of writing and make the component more modular.

Utility

Sometimes you may want to make changes only for a certain style in a specific place. In that case, utility (namespace: u-) classes can help us update it without changing the whole CSS structure.

For example, the font-size of the accordion heading is set to 32px.

.c-accordion__heading { font-size: rem(32); }

But if the font size is only different in the news section of your site and does not change anywhere else, then you might want to apply the utility class instead of setting higher specificity with the parent class or scope class.

<button aria-expanded="false" class="c-accordion__heading u-font-size--24" aria-controls="sect1" id="accordion1id" type="button">...</button> .u-font-size--24 { font-size: rem(24) !important; }

Please note that we all know !important is BAD but I added !important to the value. It is because when we use the utility class, we are absolutely sure we want the specific style to be updated as we want. Also the utility class should overwrite any other styles, so having the !important actually works well here for the utility classes. That said, the utility classes should only play a role as a helper. It should never be used for structuring your CSS.

Like scope classes, if you are using too many utility classes, you should check with designer if the design can be more consistent across the site.

Extra Namespace

On top of the namespaces we discussed above, there are two more I often use:

  • is-: this indicates the state of the block or element. Most commonly used class is .is-active, like the active link in navigation.
  • js-: this indicates that the specific element is bound to JavaScript events. For example, js-menu-click indicates that the element is bound to the click event.
Linting

Finally making rules with .stylelint and .eslint can significantly improve the code quality.

In the frontend workflow, I don’t make it as a recommendation; I make it mandatory so that failing of the rules won’t get approved.

In this way, we can ensure that the coding quality stays at its best and provide better code to other developers, including your future self.

In Action

In this section, I’d like to discuss how we could use SEM and BIO. I have made a simple, practical example to get us started:

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

The main practice with the example is to build an accordion that can be used as:

  • a normal accordion but with different color themes in the main section
  • a menu in the sidebar
  • a block displaying social media icons in the footer

What we’re achieving is a component that is:

  • Scalable: as it can be added in any part of the page without any coding changes
  • Extensible: as it can serve different functionalities with the core functions unchanged
  • Maintainable: as it is organized in a way that makes sense

To achieve SEM, BIO has been used including:

  • BEM: .c-accordion as a block and its children as elements, also used modifiers, e.g., .c-accordion--light and .c-accordion--dark
  • ITCSS: The ordering/sorting of SASS files handles the CSS specificity quite well. For example, the accordion button in the sidebar contains class="c-accordion__trigger p-sidebar-menu__button" which the pattern (p-) overwrites the component (c-) with no issues.
  • OOCSS: the accordion is constructed with several classes, for example, class="c-accordion c-accordion--dark c-accordion--single" which creates a dark theme with opening a single panel only each time.
Final thoughts

I have used this approach for almost all of my projects including universities, government departments, commercial retailers, and many other websites. In each case, I have successfully delivered all of the projects to the clients (almost no issues during the client approval stage and delivered on time); this approach has worked for me very well so far and I think it could for you as well.

That being said, technologies always change (especially on the front end) and I am more than happy to hear and discuss any of your ideas/approaches/strategies that worked for you. Let me know in the comments!

The post Combining the Powers of SEM and BIO for Improving CSS appeared first on CSS-Tricks.

HSL() / HSLa() is great for programmatic color control

Css Tricks - Fri, 06/01/2018 - 11:51am

If you ever need to hand-manipulate a color in native CSS, HSL is pretty much the only way. HSL (the hsl() and hsla() functions in CSS) stands for hue, saturation, lightness, and optionally, alpha. We've talked about it before but we can break it down a little more and do some interesting things with it.

  • Hue: Think of a color wheel. Around 0o and 360o are reds. 120o is where greens are and 240o are blues. Use anything in between 0-360. Values above and below will be modulus 360.
  • Saturation: 0% is completely desaturated (grayscale). 100% is fully saturated (full color).
  • Lightness: 0% is completely dark (black). 100% is completely light (white). 50% is average lightness.
  • alpha: Opacity/Transparency value. 0 is fully transparent. 1 is fully opaque. 0.5 is 50% transparent.

You can hand-manipulate any of those four values and have a decent sense of what is going to happen. Change the hue to take a trip around the color wheel. Change the saturation to get deeper or more muted colors. Change the lightness to essentially mix in black or white.

You might have some mental chops with rgb(), knowing that rgb(255, 0, 0) is clearly red or rgb(0, 0, 0) is black, but manipulating those to get to a light purple or starting with a forest green and getting a little lighter isn't exactly mental math. You might even be the clever sort who can identify color by Hex codes. Ask David DeSandro at a party sometime. Still, nothing nearly as intuitive as HSL.

Those of you on the cutting edge might recall the working draft of Color Level 4 with the color() function and more intuitive sub-functions. Or, you might get hot and heavy with Sass color functions or your own home brew thing. More power to ya!

See the Pen Sass Color Functions by Chris Coyier (@chriscoyier) on CodePen.

I really like HSL when playing with color in JavaScript. For example, say you want to generate some different red tones. You could randomize the H, S, and L tightly around some values:

See the Pen Random Reds by Chris Coyier (@chriscoyier) on CodePen.

Not long ago, I basically did the same thing but rotated the hue to animate this stargate:

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

If you're messing with color in JavaScript and want randomization to result in pleasing colors, give PleaseJS a spin.

Need a quick color picker? I put this one together ages ago and I quickly ported it over to CodePen:

See the Pen HSL Explorer by Chris Coyier (@chriscoyier) on CodePen.

There is also the HSL Color Picker and Mothereffering HSL if you want options.

See the Pen HSL by Graham Pyne (@gpyne) on CodePen.

Wanna learn more about color on the web in general? Don't miss Sarah Drasner's A Nerd’s Guide to Color on the Web. Lots of great goodies in there to up your understanding of working with color.

See the Pen HSL by Dan Wilson (@danwilson) on CodePen.

The post HSL() / HSLa() is great for programmatic color control appeared first on CSS-Tricks.

The State of Changing Gradients with CSS Transitions and Animations

Css Tricks - Fri, 06/01/2018 - 4:30am

Back in 2012, Internet Explorer 10 came out and, among other things, it finally supported CSS gradients and, in addition to that, the ability to animate them with just CSS! No other browser supported this at the time, but I was hopeful for the future.

Sadly, six years have passed and nothing has changed in this department. Edge supports animating gradients with CSS, just like IE 10 did back then, but no other browser has added support for this. And while animating background-size or background-position or the opacity or rotation of a pseudo element layered on top can take us a long way in terms of achieving cool effects, these workarounds are still limited.

There are effects we cannot reproduce without adding lots of extra elements or lots of extra gradients, such as "the blinds effect" seen below.

The blinds effect (live demo, Edge/ IE 10+ only).

In Edge, getting the above effect is achieved with a keyframe animation:

html { background: linear-gradient(90deg, #f90 0%, #444 0) 50%/ 5em; animation: blinds 1s ease-in-out infinite alternate; } @keyframes blinds { to { background-image: linear-gradient(90deg, #f90 100%, #444 0); } }

If that seems WET, we can DRY it up with a touch of Sass:

@function blinds($open: 0) { @return linear-gradient(90deg, #f90 $open*100%, #444 0); } html { background: blinds() 50%/ 5em; animation: blinds 1s ease-in-out infinite alternate; } @keyframes blinds { to { background-image: blinds(1) } }

While we've made the code we write and what we'll need to edit later a lot more maintainable, we still have repetition in the compiled CSS and we're limited by the fact that we can only animate between stops with the same unit — while animating from 0% to 100% works just fine, trying to use 0 or 0px instead of 0% results in no animation happening anymore. Not to mention that Chrome and Firefox just flip from orange to grey with no stop position animation at all!

Fortunately, these days we have an even better option: CSS variables!

Right out of the box, CSS variables are not animatable, though we can get transition (but not animation!) effects if the property we use them for is animatable. For example, when used inside a transform function, we can transition the transform the property.

Let's consider the example of a box that gets shifted and squished when a checkbox is checked. On this box, we set a transform that depends on a factor --f which is initially 1:

.box { /* basic styles like dimensions and background */ --f: 1; transform: translate(calc((1 - var(--f))*100vw)) scalex(var(--f)); }

When the checkbox is :checked, we change the value of the CSS variable --f to .5:

:checked ~ .box { --f: .5 }

Setting a transition on the .box makes it go smoothly from one state to the other:

.box { /* same styles as before */ transition: transform .3s ease-in; }

Note that this doesn't really work in the current version of Edge due to this bug.

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

However, CSS gradients are background images, which are only animatable in Edge and IE 10+. So, while we can make things easier for ourselves and reduce the amount of generated CSS for transitions (as seen in the code below), we're still not making progress in terms of extending support.

.blinds { background: linear-gradient(90deg, #f90 var(--pos, 0%), #444 0) 50%/ 5em; transition: .3s ease-in-out; :checked ~ & { --pos: 100%; } } Open/close blinds on checking/unchecking the checkbox (live demo, Edge only).

Enter Houdini, which allows us to register custom properties and then animate them. Currently, this is only supported by Blink browsers behind the Experimental Web Platform features flag, but it's still extending support a bit from Edge alone.

The Experimental Web Platform features flag enabled in Chrome.

Going back to our example, we register the --pos custom property:

CSS.registerProperty({ name: '--pos', syntax: '<length-percentage>', initialValue: '0%' });

Note that <length-percentage> means it accepts not only length and percentage values, but also calc() combinations of them. By contrast, <length> | <percentage> only accepts length and percentage values, but not calc() combinations of them.

However, doing this doesn't make any difference in Chrome, even with the flag enabled, probably because, in the case of transitions, what's being transitioned is the property whose value depends on the CSS variable and not the CSS variable itself. And since we generally can't transition between two background images in Chrome in general, this fails as well.

It does work in Edge, but it worked in Edge even without registering the --pos variable because Edge allows us to transition between gradients in general.

What does work in Blink browsers with the flag enabled is having an animation instead of a transition.

html { background: linear-gradient(90deg, #f90 var(--pos, 0%), #444 0) 50%/ 5em; animation: blinds .85s ease-in-out infinite alternate; } @keyframes blinds { to { --pos: 100%; } }

However, this is now not working in Edge anymore because, while Edge can animate between gradient backgrounds, it cannot do the same for custom properties.

So we need to take an alternative approach for Edge here. This is where @supports comes in handy, since all we have to do is check whether a -ms- prefixed property is supported.

@function grad($pos: 100%) { @return linear-gradient(90deg, #f90 $pos, #444 0); } html{ /* same as before */ @supports (-ms-user-select: none) { background-image: grad(0%); animation-name: blinds-alt; } } @keyframes blinds-alt { to { background-image: grad() } }

Stop positions aren't the only thing we can animate this way. We can do the same thing for the gradient angle. The idea behind it is pretty much the same, except now our animation isn't an alternating one anymore and we use an easeInOutBack kind of timing function.

@function grad($ang: 1turn) { @return linear-gradient($ang, #f90 50%, #444 0); } html { background: grad(var(--ang, 0deg)); animation: rot 2s cubic-bezier(.68, -.57, .26, 1.65) infinite; @supports (-ms-user-select: none) { background-image: grad(0turn); animation-name: rot-alt; } } @keyframes rot { to { --ang: 1turn; } } @keyframes rot-alt { to { background-image: grad(); } }

Remember that, just like in the case of stop positions, we can only animate between gradient angles expressed in the same unit in Edge, so calling our Sass function with grad(0deg) instead of grad(0turn) doesn't work.

And, of course, the CSS variable we now use accepts angle values instead of lengths and percentages:

CSS.registerProperty({ name: '--ang', syntax: '<angle>', initialValue: '0deg' }); Sweeping around (live demo, Blink browsers with flag and Edge only).

In a similar fashion, we can also animate radial gradients. And the really cool thing about the CSS variable approach is that it allows us to animate different components of the gradient differently, which is something that's not possible when animating gradients as a whole the way Edge does (which is why the following demos don't work as well in Edge).

Let's say we have the following radial-gradient():

$p: 9%; html { --x: #{$p}; --y: #{$p}; background: radial-gradient(circle at var(--x) var(--y), #f90, #444 $p); }

We register the --x and --y variables:

CSS.registerProperty({ name: '--x', syntax: '<length-percentage>', initialValue: '0%' }); CSS.registerProperty({ name: '--y', syntax: '<length-percentage>', initialValue: '0%' });

Then we add the animations:

html { /* same as before */ animation: a 0s ease-in-out -2.3s alternate infinite; animation-name: x, y; animation-duration: 4.1s, 2.9s; } @keyframes x { to { --x: #{100% - $p} } } @keyframes y { to { --y: #{100% - $p} } }

The result we get can be seen below:

Moving light (live demo, Blink browsers with flag only).

We can use this technique of animating the different custom properties we use inside the gradient function to make the blinds in our initial example close the other way instead of going back. In order to do this, we introduce two more CSS variables, --c0 and --c1:

$c: #f90 #444; html { --c0: #{nth($c, 1)}; --c1: #{nth($c, 2)}; background: linear-gradient(90deg, var(--c0) var(--pos, 0%), var(--c1) 0) 50%/ 5em; }

We register all these custom properties:

CSS.registerProperty({ name: '--pos', syntax: '<length-percentage>', initialValue: '0%' }); CSS.registerProperty({ name: '--c0', syntax: '<color>', initialValue: 'red' }); /* same for --c1 */

We use the same animation as before for the position of the first stop --pos and, in addition to this, we introduce two steps() animations for the other two variables, switching their values every time an iteration of the first animation (the one changing the value of --pos) is completed:

$t: 1s; html { /* same as before */ animation: a 0s infinite; animation-name: c0, pos, c1; animation-duration: 2*$t, $t; animation-timing-function: steps(1), ease-in-out; } @keyframes pos { to { --pos: 100%; } } @keyframes c0 { 50% { --c0: #{nth($c, 2)} } } @keyframes c1 { 50% { --c1: #{nth($c, 1)} } }

And we get the following result:

Another version of the blinds animation (live demo, Blink browsers with flag only).

We can also apply this to a radial-gradient() (nothing but the background declaration changes):

background: radial-gradient(circle, var(--c0) var(--pos, 0%), var(--c1) 0); Growing discs (live demo, Blink browsers with flag only).

The exact same tactic works for conic-gradient() as well:

background: conic-gradient(var(--c0) var(--pos, 0%), var(--c1) 0); Growing slices (live demo, Blink browsers with flag only).

Repeating gradients are also an option creating a ripple-like effect in the radial case:

$p: 2em; html { /* same as before */ background: repeating-radial-gradient(circle, var(--c0) 0 var(--pos, 0px), var(--c1) 0 $p); } @keyframes pos { 90%, 100% { --pos: #{$p} } } Ripples (live demo, Blink browsers with flag only).

And a helix/rays effect in the conic case:

$p: 5%; html { /* same as before */ background: repeating-conic-gradient( var(--c0) 0 var(--pos, 0%), var(--c1) 0 $p); } @keyframes pos { 90%, 100% { --pos: #{$p} } } Growing rays (live demo, Blink browsers with flag only).

We can also add another CSS variable to make things more interesting:

$n: 20; html { /* same as before */ background: radial-gradient(circle at var(--o, 50% 50%), var(--c0) var(--pos, 0%), var(--c1) 0); animation: a 0s infinite; animation-name: c0, o, pos, c1; animation-duration: 2*$t, $n*$t, $t; animation-timing-function: steps(1), steps(1), ease-in-out; } @keyframes o { @for $i from 0 to $n { #{$i*100%/$n} { --o: #{random(100)*1%} #{random(100)*1%} } } }

We need to register this variable for the whole thing to work:

CSS.registerProperty({ name: '--o', syntax: '<length-percentage>+', initialValue: '50%' });

And that's it! The result can be seen below:

Randomly positioned growing discs (live demo, Blink browsers with flag only).

I'd say the future of changing gradients with keyframe animations looks pretty cool. But in the meanwhile, for cross-browser solutions, the JavaScript way remains the only valid one.

The post The State of Changing Gradients with CSS Transitions and Animations appeared first on CSS-Tricks.

Frustration

Css Tricks - Thu, 05/31/2018 - 8:26am

Jeremy Keith talks about a couple of recent frustrating moments in his life. One regarding a musical instrument, one involving a build process:

That feeling of frustration I get from having wiring issues with a musical instrument is the same feeling I get whenever something goes awry with my web server. I know just enough about servers to be dangerous. When something goes wrong, I feel very out of my depth, and again, I have no idea how long it will take the fix the problem: minutes, hours, days, or weeks.

I echo his later sentiment that moments like these become great writing opportunities.

I'd say that it's always OK to experience frustration. It doesn't make you a lesser developer, at any level. But at the same time, the more experienced of a developer you become, less things will trigger that frustration, because of the resources you've built up to deal with those situations. Notice Jeremy didn't give up and a co-worker came to the rescue.

Direct Link to ArticlePermalink

The post Frustration appeared first on CSS-Tricks.

Forms, Auth and Serverless Functions on Gatsby and Netlify

Css Tricks - Thu, 05/31/2018 - 5:45am

Abstracting infrastructure is in our DNA. Roads, schools, water supply networks—you get the idea. Web development is no exception: serverless architectures are a beautiful expression of that phenomenon. Static sites, in particular, are turning into dynamic, rich experiences.

Handling static forms, authentication, and backend functions on statically-generated sites is now a thing. Especially with the JAMstack pioneer platform that is Netlify. Recently, they announced support of AWS Lambda functions on front-end-centric sites and apps. I've been meaning to dive into their "backend" features since.

Today, I'm doing just that, using a static Gatsby site, Netlify's Forms, Identity, and Functions features. This tutorial will show you how to:

  • Add static forms to your site
  • Add user authentication for password-protected content
  • Create an AWS Lambda function

Ready to supercharge a static site with serverless features?

Consider checking out Netlify's React-powered static CMS after this post! And here’s a tutorial on a whole comment workflow, complete with an approval system, using the JAMstack.

Static site forms, auth, and AWS Lambda functions

Before diving into the code, let's detail our use case. I'll be using three different Netlify serverless features:

1. Identity

Identity will be used to create a password-protected, gated content section on the Gatsby site. Authentication without a backend has long been a pain on static sites. But this neat feature solves it elegantly, allowing developers to:

Manage signups, logins, password recovery, and more — all without rolling your own authentication service.

2. Forms

Forms will be used to enable user-submitted product reviews on the site. Dynamic forms can take many forms (see what I did there?), from simple contact forms to commenting, quoting, and review systems hooked to internal tools.

There's a plethora of solutions to handle interactive forms on static sites. But with Forms you can handle them directly within your building and hosting service (Netlify's core offering). No need for spam trap mailto: links, configuring your own server, setting up serverless functions, or integrating third parties like Formspree or FormKeep.

No JavaScript, APIs, or backend needed: just a simple HTML form tagged with the netlify HTML attribute.

3. Functions

Functions will be used to set up a reviews moderation workflow directly in Slack. Under this feature's hood are AWS Lambda functions—event-triggered, scalable backend code you can run without your own server. Deploying these with Netlify is as simple as adding a file to a Git repo.

Lambda functions are indeed powerful, but they normally require an AWS account and API gateway configuration. Like with Forms, Functions simplify your life by offloading the legwork to Netlify:

Your functions are version-controlled, built, and deployed along with the rest of your Netlify site, and the Netlify API gateway automatically handles service discovery. Plus, your functions benefit from the power of Deploy Previews and rollbacks.

In a nutshell, Functions allow you to enhance site interactivity + bridge the front and backend to let data flow between services.

Serverless Gatsby on Netlify: auth, static forms, and Lambda functions

I'll jumpstart this tutorial by using a stripped down version of a previous Gatsby site we built.

To learn Gatsby basics, check out the official tutorial. We also have two e-commerce tutorials with Gatsby here and here.

Prerequisites

For this tutorial, you'll need:

1. Forking the Gatsby project

Start by forking the repo:

View on GitHub

I suggest playing around with it to get familiar with the project. Products are in the src/data/products folder—all inside Markdown files. These files are loaded during the build time and used to inject the proper info in our templates. This is done inside the gatsby-node.js file.

2. Adding Identity for authentication

If you opened any product file, you probably saw there's a field we don't usually use in Snipcart demos: a private attribute. The goal is simple: show these "exclusive" products only when a user is logged in.

To handle that, I used Netlify's Identity widget, a simple way of adding authentication to a static website.

You can install the Identity package by running:

npm install --save netlify-identity-widget

Make sure to include it in your header! Inside the src/components/Header/index.js file, I added this line after the h1 closing tag:

<div data-netlify-identity-menu></div>

You can then import the widget at the top of your file with

const netlifyIdentity = require("netlify-identity-widget");

And declare a componentDidMount function as such:

componentDidMount(){ netlifyIdentity.init(); }

The Identity widget now injects proper login forms inside that <div>. Now that you have a static form to login, you need the appropriate logic to validate if a user is logged in or not.

I used this logic to show appropriate, password-protected products to logged in users. To do so, I created a products.js inside the src/pages folder, and defined the following component:

import React from 'react' import Link from 'gatsby-link' import styles from './products.module.css' const netlifyIdentity = require("netlify-identity-widget"); export default class Products extends React.Component { constructor(data){ super(data); this.state = { products: [] } } getProducts(){ return netlifyIdentity.currentUser() != null ? this.props.data.allMarkdownRemark.edges : this.props.data.allMarkdownRemark.edges .filter(x => !x.node.frontmatter.private) } updateProducts(){ this.setState({ products: this.getProducts() }); } componentDidMount(){ netlifyIdentity.on("login", user => this.updateProducts()); netlifyIdentity.on("logout", () => this.updateProducts()); this.updateProducts(); } render(){ return ( <div> <h1>Products</h1> <p>To login use the email: geeks@snipcart.com with password: admin</p> <ul className={styles.itemsList}> {this.state.products.map((o, index) => <li key={index} className={styles.item}> <Link to={o.node.frontmatter.loc}> <figure> <img className={styles.image} src={o.node.frontmatter.image} alt={o.node.frontmatter.name}></img> <figcaption className={styles.figCaption}>Buy the {o.node.frontmatter.name} now</figcaption> </figure> </Link> </li> )} </ul> </div>) } } export const query = graphql` query allProducts { allMarkdownRemark { edges { node { frontmatter { sku, loc, price, desc, private, name, image } } } } `

I won't explain the GraphQL bit here. If you're interested, read more here.

The important part to understand is what's happening inside the componentDidMountlifecycle function. I'm binding myself to the widget 'login' and 'logout' events to update available products.

The end-result is pretty awesome:

3. Handling static forms for reviews

To add product reviews to the Gatsby site, I used Netlify's Forms. You can include their Forms on your own site by adding a 'data-netlify="true"' (or just netlify) attribute to a form declaration. I included it in my src/components/product.js file, after the last section tag.

You will also need to declare a formId variable before the return of your render function such as:

render(){ if(this.props.data.markdownRemark.frontmatter.private && !this.state.loggedIn){ return fourOfour(); } var formId = `product-${this.props.data.markdownRemark.frontmatter.sku}` const button = this.props.data.markdownRemark.frontmatter.private ? ( <button type="button" className={`${styles.buyButton}`}> SOLD OUT </button> ) : ( <button type="button" className={`${styles.buyButton} snipcart-add-item`} data-item-name={this.props.data.markdownRemark.frontmatter.name} data-item-id={this.props.data.markdownRemark.frontmatter.sku} data-item-image={this.props.data.markdownRemark.frontmatter.image} data-item-url={`${NETLIFY_URL}${this.props.location.pathname}`} data-item-price={this.props.data.markdownRemark.frontmatter.price} data-item-description={this.props.data.markdownRemark.frontmatter.desc}> Buy it now for {this.props.data.markdownRemark.frontmatter.price}$ </button> ); return ( <div> <h1>{this.props.data.markdownRemark.frontmatter.name}</h1> <div className={styles.breadcrumb}> <Link to='/'>Back to the products</Link> </div> <p>{this.props.data.markdownRemark.frontmatter.desc}</p> <section className="section__product"> <figure className={styles.productFigure}> <img src={this.props.data.markdownRemark.frontmatter.image} /> </figure> <article> {this.props.data.markdownRemark.frontmatter.description} </article> <div className={styles.actions}> {button} </div> </section> <section> <h3 className="reviews">Reviews</h3> <div className="reviews__list"> {this.state.reviews.map((o) => <p key={o.number}> <div className="review__name">{o.name}</div> <div>{o.data.message}</div> </p> )} </div> <form className="review__form" name={formId} method="POST" data-netlify-honeypot="bot-field" data-netlify="true"> <input type="hidden" name="form-name" value={formId} /> <div className="field__form"> <label>NAME</label> <input type="text" name="name"></input> </div> <div className="field__form"> <label>EMAIL</label> <input type="email" name="email"></input> </div> <div className="field__form"> <label>MESSAGE</label> <textarea name="message"></textarea> </div> <button className="button__form" type="submit">SEND</button> </form> </section> </div>) }

Boom, static forms are on your site!

However, to show submissions coming in through these forms, you need a Netlify function to fetch and return user reviews. To do so, I created a netlify.toml file with the following content:

[build] functions = "functions"

I then put a functions folder directly in the root project. Inside it, I placed a fetchreviews.js file with:

const https = require('https'); exports.handler = function(event, context, callback) { var id = event.queryStringParameters.id; var token = process.env.netlify_access_token; if(id == undefined){ callback('A product id must be specified.', { statusCode: 500 }) } var options = { hostname: 'api.netlify.com', port: 443, method: 'GET', headers: { 'Content-Type': 'application/json' } }; var queryToken = `access_token=${token}`; var opts1 = Object.assign({}, options, { path: `/api/v1/sites/${process.env.site_id}/forms?${queryToken}`}); var req = https.request(opts1, function(res) { res.setEncoding('utf8'); var body = ""; res.on('data', data => { body += data; }); res.on('end', function () { body = JSON.parse(body); var form = body.filter(x => x.name == `product-${id}`)[0]; var opts2 = Object.assign({}, options, { path: `/api/v1/forms/${form.id}/submissions?${queryToken}`}); var req2 = https.request(opts2, function(res2) { res2.setEncoding('utf8'); var body2 = ""; res2.on("data", (data) => { body2 += data; }); res2.on('end', function () { callback(null, { statusCode: 200, headers: { "Access-Control-Allow-Origin" : "*", 'Content-Type': 'application/json' }, body: body2 }) }); }); req2.end(); }); }); req.end(); }

The function checks whether a product ID was given as a query parameter. If there is an ID, it fetches the form with the name product-{product-id} to get all the reviews from it. This way I could show reviews in the front end.

I added two functions to product.js to do so:

constructor(props){ super(props); this.state = { reviews: [], loggedIn: false } } componentDidMount(){ fetch(`https://${NETLIFY_FUNC}/fetchreviews?id=${this.props.data.markdownRemark.frontmatter.sku}`) .then(x => x.json()) .then(x => { this.setState({reviews: x}) }) if(netlifyIdentity.currentUser() != null){ this.setState({loggedIn: true}); } netlifyIdentity.on("login", user => this.setState({loggedIn: true})); netlifyIdentity.on("logout", () => this.setState({loggedIn: false})); }

Then, just before the review form:

{this.state.reviews.map((o) => <p key={o.number}>{o.name}: {o.data.message}</p> )}

Above, the mounted component fetches the new functions to get specific product reviews. It also updates the state, and show them on the pages. You can also see that we decided to put a "Sold out" button for private products, this is because these are private and wouldn't pass our validation if we were to simply put the current URL, we could still do it but it would require a bit more work out of the scope of this demo.

If you want to test your functions without deploying to Netlify, use the netlify-lambda node package to do so locally. Once you've installed it (npm install netlify-lambda) run netlify-lambda serve ./ in your project folder. The function will run at http://localhost:9000/fetchreviews.

You can update the fetch route above and get the same behavior you'd have with a hosted function.

4. Configuring an AWS Lambda function with Slack

You will need Netlify Forms Pro in order to trigger a function on form submissions.

Last but not least: the reviews moderation workflow directly in Slack. The goal is simple: push review details and notification to Slack and allow to either *keep* or *reject* review from Slack.

To do so, I created 2 new functions in the functions folder: notifyslack.js and answerslack.js. The first is notified by Netlify's webhook of each form submission and is in charge of communicating this to Slack with appropriate action items. I created a small Slack app for this to work (reference).

Here are the permissions the app needs:

Interactive components config:

The Request URL field is where your Netlify function can be called.

With these set up, I installed my app and opened the Incoming webhooks tab. I copied the webhook URL, and got back to my project.

Inside functions, I created the notifyslack.js file with:

var https = require("https"); exports.handler = function(event, context, callback) { var body = JSON.parse(event.body); if(body != null && body.data != null){ var data = body.data; var message = `New review from ${data.email} \n ${data.name}: ${data.message}`; var attach = [ { "title": "Review ID", "text": body.id }, { "title": "Do you want to keep the review?", "text": message, "fallback": "You can't take actions for this review.", "callback_id": "answer_netlify", "color": "#3AA3E3", "attachment_type": "default", "actions": [ { "name": "response", "text": "Keep", "type": "button", "value": "keep" }, { "name": "response", "text": "Reject", "type": "button", "style": "danger", "value": "reject", "confirm": { "title": "Are you sure?", "text": "Once it's done the review will be deleted", "ok_text": "Yes", "dismiss_text": "No" } } ] } ] var postData = JSON.stringify({ attachments: attach }); var options = { hostname: 'hooks.slack.com', port: 443, path: process.env.slack_webhook_url, method: 'POST', headers: { 'Content-Type': 'application/json' } }; var req = https.request(options, function(res) { res.setEncoding('utf8'); res.on('end', function () { callback(null, { statusCode: 200 }) }); }); req.on('error', function (e) { console.log('Problem with request:', e.message); }); req.write(postData); req.end(); callback(null, { statusCode: 200 }) } }

Here, you need to update the path value of the options object with your corresponding Slack app webhook URL.

At the moment, this would only notify Slack—whatever action you chose wouldn't trigger anything else.

To make the Slack notification interactive, I created a 3rd function inside a file called answerslack.js. This function is probably the most complicated, but it's mostly requests overhead, so bear with me:

var https = require("https"); var qs = require('querystring') function getURL(href) { var match = href.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/); return match && { href: href, protocol: match[1], host: match[2], hostname: match[3], port: match[4], pathname: match[5], search: match[6], hash: match[7] } } exports.handler = function(event, context, callback) { var json = JSON.parse(qs.parse(event.body).payload); var answer = json.actions[0].value; var access_token = process.env.netlify_access_token; var id = json.original_message.attachments[0].text; if(answer == 'reject'){ var options = { hostname: 'api.netlify.com', port: 443, path: `/api/v1/submissions/${id}?access_token=${access_token}`, method: 'DELETE', headers: { 'Content-Type': 'application/json' } }; var req1 = https.request(options, function(res) { res.setEncoding('utf8'); res.on('end', function () { console.log(`Review with id: ${id} was deleted successfully.`) }); }); req1.on('error', function (e) { console.log('Problem with request:', e.message); }); req1.end(); } var postData = JSON.stringify({ replace_original: true, attachments: [{ text: answer == 'keep' ? `The review (${id}) was approved!` : `The review (${id}) was rejected.` }] }); var url = getURL(json.response_url); var options = { hostname: url.hostname, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json' } }; var req = https.request(options, function(res) { res.setEncoding('utf8'); res.on('end', function () { callback(null, { statusCode: 200 }) }); }); req.on('error', function (e) { console.log('Problem with request:', e.message); }); req.write(postData); req.end(); callback(null, { statusCode: 200 }) }

I parse the event payload and check whether the action value was reject. If it's not, it's necessarily keep—nothing to do. But if it is, I need to call Netlify's API to delete the rejected review. I'm retrieving the review ID put earlier inside the first attachment text with the following line:

json.original_message.attachments[0].text;

Once done, I can delete it with one API call. I then give our Slack users feedback by calling the response URL.

I'm kind of proud of the final workflow here TBH:

5. Deploying the Gatsby site on Netlify

Here, I pushed everything to GitHub and hooked it up to Netlify using these settings:

You can see we've used some of the environment variables with the process.env.{variable} notation in the demo.

These are private settings we don't want to make public. To define yours directly, go to /settings/deploys in Netlify's dashboard, hit Edit variables, and input the following:

  • netlify_access_token: The token created earlier inside netlify
  • site_id: Your website url without the protocol
  • slack_webhook_url: Your Slack app webhook URL

Website deployed; time to play around!

Read this to learn how to deploy a site on Netlify.

Live demo and GitHub repo

Live Demo

GitHub Repo

Closing thoughts

This demo took me way more time than expected—a bit of docs misreading on my part, I'll admit.

The challenge was mostly to bundle all services together in good fashion. The biggest hurdle was that functions were not loading properly when launched to Netlify; they would error on each call. For some reason, my project node dependencies were not accessible. I decided to ditch the package I was using to make my requests and use the good ol' httpsnative package instead.

Also, constantly pushing to Netlify to test my functions for fear of them not behaving the same as locally was a bit of a pain.

Now, a few tweaks could be done to improve upon this demo, but overall, I'm super satisfied. Pretty dynamic as far as static sites go, wouldn't you say? :)

I sincerely hope this helps developers get started with Netlify's backend features. If you do build similar JAMstack projects, make sure to send them our way.

We'd love to dig in your code!

The post Forms, Auth and Serverless Functions on Gatsby and Netlify appeared first on CSS-Tricks.

?Customize payment solutions with our enhanced platform

Css Tricks - Thu, 05/31/2018 - 5:17am

(This is a sponsored post.)

We’ve upped our game by using developers’ feedback to improve the Authorize.Net payment platform. Check out our new, streamlined API, better sample code and SDKs, and use them to provide your merchants with a secure, scalable payment solution. You’ll see that it’s a seamless and efficient way to make sure you and your merchants get paid!

Start playing

Direct Link to ArticlePermalink

The post ?Customize payment solutions with our enhanced platform appeared first on CSS-Tricks.

Transmit Droplets

Css Tricks - Wed, 05/30/2018 - 11:42am

Ethan Marcotte documented his workflow for storing GIFs in a web directory. Sometimes just SFTPing files into a folder is as fancy a workflow as you need, and in fact, modern workflows don't have anything on it!

I've also used Transmit's fancy features for this kind of thing. I prefer saving the connection as a Droplet, which is basically a little application you can drop a file onto and have it upload to exactly where you want it...say a GIF in a specific server directory.

There are two things that make this even more useful. One, you can have it copy the URL after uploading:

Two, you can set rules for file types so that they have the right permissions as soon as they are uploaded. I find this particularly useful for S3 where by default they aren't "world readable" and need to be changed manually. This rule prevents that from being a manual step every time.

The post Transmit Droplets appeared first on CSS-Tricks.

User Experience And Common Sense

Usability Geek - Wed, 05/30/2018 - 9:25am
User Experience. Is this term just another word for common sense? As a discipline, after all, user experience (UX) is dedicated to creating intuitive, simple and straightforward experiences. Good UX...
Categories: Web Standards

What does the ‘h’ stand for in Vue’s render method?

Css Tricks - Wed, 05/30/2018 - 4:13am

If you’ve been working with Vue for a while, you may have come across this way of rendering your app — this is the default in the latest version of the CLI, in main.js:

new Vue({ render: h => h(App) }).$mount('#app')

Or, if you’re using a render function, possibly to take advantage of JSX:

Vue.component('jsx-example', { render (h) { return <div id="foo">bar</div> } })

You may be wondering, what does that h do? What does it stand for? The h stands for hyperscript. It’s a riff of HTML, which means Hypertext Markup Language: since we’re dealing with a script, it’s become convention in virtual DOM implementations to use this substitution. This definition is also addressed in the documentation of other frameworks as well. Here it is, for example, in Cycle.js.

In this issue, Evan describes that:

Hyperscript itself stands for "script that generates HTML structures"

This is shortened to h because it’s easier to type. He also describes it a bit more in his Advanced Vue workshop on Frontend Masters.

Really, you can think of it as being short for createElement. Here would be the long form:

render: function (createElement) { return createElement(App); }

If we replace that with an h, then we first arrive at:

render: function (h) { return h(App); }

...which can then be shortened with the use of ES6 to:

render: h => h (App)

The Vue version takes up to three arguments:

render(h) { return h('div', {}, [...]) }
  1. The first is type of the element (here shown as div).
  2. The second is the data object. We nest some fields here, including: props, attrs, dom props, class and style.
  3. The third is an array of child nodes. We’ll then have nested calls and eventually return a tree of virtual DOM nodes.

There’s more in-depth information in the Vue Guide here.

The name hyperscript may potentially be confusing to some people, given the fact that hyperscript is actually the name of a library (what isn’t updated these days) and it actually has a small ecosystem. In this case, we’re not talking about that particular implementation.

Hope that clears things up for those who are curious!

The post What does the ‘h’ stand for in Vue’s render method? appeared first on CSS-Tricks.

Keep Their Eyes On The Prize: Visual Hierarchy In UX Design

Usability Geek - Tue, 05/29/2018 - 12:01pm
How are you reading this sentence, right now? I do not mean on a laptop, or on your smartphone. I mean how are you reading? How exactly is information captured by the eyes and processed by the brain?...
Categories: Web Standards

Managing State in React With Unstated

Css Tricks - Tue, 05/29/2018 - 4:04am

As your application becomes more complex, the management of state can become tedious. A component's state is meant to be self-contained, which makes sharing state across multiple components a headache. Redux is usually the go-to library to manage state in React, however, depending on how complex your application is, you might not need Redux.

Unstated is an alternative that provides you with the functionality to manage state across multiple components with a Container class and Provider and Subscribe components. Let's see Unstated in action by creating a simple counter and then look at a more advanced to-do application.

Using Unstated to Create a Counter

The code for the counter we’re making is available on GitHub:

View Repo

You can add Unstated to your application with Yarn:

yarn add unstated Container

The container extends Unstated's Container class. It is to be used only for state management. This is where the initial state will be initialized and the call to setState() will happen.

import { Container } from 'unstated' class CounterContainer extends Container { state = { count: 0 } increment = () => { this.setState({ count: this.state.count + 1 }) } decrement = () => { this.setState({ count: this.state.count - 1 }) } } export default CounterContainer

So far, we’ve defined the Container (CounterContainer), set its starting state for count at the number zero and defined methods for adding and subtracting to the component's state in increments and decrements of one.

You might be wondering why we haven’t imported React at this point. There is no need to import it into the Container since we will not be rendering JSX at all.

Events emitters will be used in order to call setState() and cause the components to re-render. The components that will make use of this container will have to subscribe to it.

Subscribe

The Subscribe component is used to plug the state into the components that need it. From here, we will be able to call the increment and decrement methods, which will update the state of the application and cause the subscribed component to re-render with the correct count. These methods will be triggered by a couple of buttons that contain events listeners to add or subtract to the count, respectively.

import React from 'react' import { Subscribe } from 'unstated' import CounterContainer from './containers/counter' const Counter = () => { return ( <Subscribe to={[CounterContainer]}> {counterContainer => ( <div> <div> // The current count value Count: { counterContainer.state.count } </div> // This button will add to the count <button onClick={counterContainer.increment}>Increment</button> // This button will subtract from the count <button onClick={counterContainer.decrement}>Decrement</button> </div> )} </Subscribe> ) } export default Counter

The Subscribe component is given the CounterContainer in the form of an array to its to prop. This means that the Subscribe component can subscribe to more than one container, and all of the containers are passed to the to prop of the Subscribe component in an array.

The counterContainer is a function that receives an instance of each container the Subscribe component subscribes to.

With that, we can now access the state and the methods made available in the container.

Provider

We'll make use of the Provider component to store the container instances and allow the children to subscribe to it.

import React, { Component } from 'react'; import { Provider } from 'unstated' import Counter from './Counter' class App extends Component { render() { return ( <Provider> <Counter /> </Provider> ); } } export default App;

With this, the Counter component can make use of our counterContainer.

Unstated allows you to make use of all the functionality that React's setState() provides. For example, if we want to increment the previous state by one three times with one click, we can pass a function to setState() like this:

incrementBy3 = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) }

The idea is that the setState() still works like it does, but this time with the ability to keep the state contained in a Container class. It becomes easy to spread the state to only the components that need it.

Let’s Make a To-Do Application!

This is a slightly more advanced use of Unstated. Two components will subscribe to the container, which will manage all of the state, and the methods for updating the state. Again, the code is available on Github:

View Repo

The container will look like this:

import { Container } from 'unstated' class TodoContainer extends Container { state = { todos: [ 'Mess around with unstated', 'Start dance class' ], todo: '' }; handleDeleteTodo = (todo) => { this.setState({ todos: this.state.todos.filter(c => c !== todo) }) } handleInputChange = (event) => { const todo = event.target.value this.setState({ todo }); }; handleAddTodo = (event) => { event.preventDefault() this.setState(({todos}) => ({ todos: todos.concat(this.state.todo) })) this.setState({ todo: '' }); } } export default TodoContainer

The container has an initial todos state which is an array with two items in it. To add to-do items, we have a todo state set to an empty string.

We’re going to need a CreateTodo component that will subscribe to the container. Each time a value is entered, the onChange event will trigger then fire the handleInputChange() method we have in the container. Clicking the submit button will trigger handleAddTodo(). The handleDeleteTodo() method receives a to-do and filters out the to-do that matches the one passed to it.

import React from 'react' import { Subscribe } from 'unstated' import TodoContainer from './containers/todoContainer' const CreateTodo = () => { return ( <div> <Subscribe to={[TodoContainer]}> {todos => <div> <form onSubmit={todos.handleAddTodo}> <input type="text" value={todos.state.todo} onChange={todos.handleInputChange} /> <button>Submit</button> </form> </div> } </Subscribe> </div> ); } export default CreateTodo

When a new to-do is added, the todos state made available in the container is updated. The list of todos is pulled from the container to the Todos component, by subscribing the component to the container.

import React from 'react'; import { Subscribe } from 'unstated'; import TodoContainer from './containers/todoContainer' const Todos = () => ( <ul> <Subscribe to={[TodoContainer]}> {todos => todos.state.todos.map(todo => ( <li key={todo}> {todo} <button onClick={() => todos.handleDeleteTodo(todo)}>X</button> </li> )) } </Subscribe> </ul> ); export default Todos

This component loops through the array of to-dos available in the container and renders them in a list.

Finally, we need to wrap the components that subscribe to the container in a provider like we did in the case of the counter. We do this in our App.js file exactly like we did in the counter example:

import React, { Component } from 'react'; import { Provider } from 'unstated' import CreateTodo from './CreateTodo' import Todos from './Todos' class App extends Component { render() { return ( <Provider> <CreateTodo /> <Todos /> </Provider> ); } } export default App; Wrapping Up

There are different ways of managing state in React depending on the complexity of your application and Unstated is a handy library that can make it easier. It’s worth reiterating the point that Redux, while awesome, is not always the best tool for the job, even though we often grab for it in these types of cases. Hopefully you now feel like you have a new tool in your belt.

The post Managing State in React With Unstated appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.