Archiwum kategorii: CSS

Variable Fonts with Jason Pamental

Post pobrano z: Variable Fonts with Jason Pamental

We’ve already hit you with a one-two punch of variable fonts today. Robin shared Richard Rutter’s post on real-world usage of them. Ollie Williams introduced us to the in’s-and-out’s of using them on the web.

I figured we’d make it a trifecta and link up our discussion about variable fonts with Jason Pamental. Dave and I talk with Jason for an entire hour digging into the real story, possibilities, and future of all this variable fonts business. Don’t miss his or Mandy Michael’s demo Collections either.

Direct Link to Article — Permalink


Variable Fonts with Jason Pamental is a post from CSS-Tricks

One File, Many Options: Using Variable Fonts on the Web

Post pobrano z: One File, Many Options: Using Variable Fonts on the Web

In 2016, an important development in web typography was jointly announced by representatives from Adobe, Microsoft, Apple, and Google. Version 1.8 of the OpenType font format introduced variable fonts. With so many big names involved, it’s unsurprising that all browsers are on-board and racing ahead with implementation.

Font weights can be far more than just bold and normal—most professionally designed typefaces are available in variants ranging from a thin hairline ultralight to a black extra-heavy bold. To make use of all those weights, we would need a separate file for each. While a design is unlikely to need every font-weight, a wider variety than bold and normal adds visual hierarchy and interest to a page.

The Google Fonts GUI makes clear: the more weights you choose, the slower your site

There’s more than various weights to consider. CSS3 introduced the font-stretch property, with values from ultra-condensed to ultra-expanded. Until now, these values only worked if you provided a separate file for each width. If you wanted every combination of weight and width in both normal and italic, you would need dozens of files.

The popular Gotham font, available in many width and weight combinations

With variable fonts, we can get all this variety with a single file.

The OpenType spec lists five standard axes of variation—all labeled by a four-character string. These are aspects of the typeface that we have control over.

  • wght – Weight is controlled by the CSS font-weight property. The value can be anything from 1 to 999. This will allow for a more granular level of control.
  • wdth – Width is controlled by the CSS font-stretch property. It can take a keyword or a percentage value. While it’s long been possible to use a transform to scaleX or scaleY, that distorts the font in ugly ways unintended by the typographer. The width axis is defined by the font designer to expand or condense elegantly.
  • opsz – Optical sizing can be turned on or off using the new font-optical-sizing property. (I’ll explain what optical sizing is later on.)
  • ital – Italicization is achieved by setting the CSS font-style property to italic
  • slnt– Slant is controlled by setting the CSS font-style property set to oblique. It will default to a 20 degree slant but it can also accept a specified degree between -90deg and 90deg.

Unfortunately, not every variable font will necessarily make use of all five axes. It’s entirely dependent on the creator of the particular typeface. After testing every variable font I could get my hands on, by far the most commonly implemented is weight, followed closely by width. Much of the time you will need two files: one for italic and one for regular, as the ital axis isn’t always implemented. As Frank Grießhammer of Adobe told me:

Italic and Roman styles have (often radically) different construction principles, therefore point structures may not always be compatible.

The browser can make any non-italic font emulate italics, but this is typographically ill-advised.

Typographers can define named instances within their variable font. A named instance is a preset—a particular variation the font is capable of accessing with a name (e.g. „Extra Light”) rather than with numbers alone. In the current CSS spec, however, there is no way to access these named instances. It’s important to note that when you use a value like extra-condensed or semi-expanded for font-stretch, the value maps to a percentage predefined in the CSS spec—not to any named instance chosen by the font creator. For font-weight, the bold value maps to 700 and normal to 400. As the spec puts it, „a font might internally provide its own mappings, but those mappings within the font are disregarded.”

The CSS Fonts Module Level 4 spec introduces the new font-variation-settings property to control variable font options. The following two CSS declarations are equivalent:

h1 {
  font-weight: 850;
  font-style: italic;
  font-stretch: normal;
}

h1 {
  font-variation-settings: "wght" 850, "wdth" 100, "ital" 1;
}

The spec strongly prefers using font-optical-sizing, font-style, font-weight and font-stretch over font-variation-settings for controlling any of the five standard axes. As Myles Maxfield kindly explained to me:

font-variation-settings is not identical to the other variation-aware properties, because with these other properties, the browser has insight into the meaning of the variations, and can therefore do things like applying them to other font file formats, or creating synthesized versions if the font file doesn’t support the axis.

Microsoft will register more standard axes tags over time. As new axes are added, we can also expect new CSS properties to control them. Font creators are also free to invent their own axes. This is why font-variation-settings was added to CSS—it is the only way to control custom axes. Lab DJR and Decovar are two typeface made with the express intention of demonstrating just how malleable a single variable font can be. Lab DJR, for example, offers four custom axes:

h1 {
  font-variation-settings: 'SIZE' 100, 'QUAD' 80, 'BEVL' 950, 'OVAL' 210;
}
Courtesy of David Jonathan Ross. David is by the typographer of Lab DJR and already has several variable fonts to his name.

These foundry-defined custom axes must use uppercase letters while the standardized axes always use lower case. With unique and unstandardized options, CSS authors must count on font developers to properly document their work.

The versatility of Decovar is the perfect showcase for the power of variable fonts being more than just a saved HTTP request

Performance

You might download a variable font in TTF format rather than as a pre-compressed file. You’ll definitely want to convert it into .woff2. Google offer a command line tool predictably named woff2 to make it easy. If you cd into the folder containing your font while in the command line, you can type:

woff2_compress examplefont.ttf

We’ve established that we’ll only need one HTTP request per typeface (or possibly two to separate Roman and Italic styles). Because they’re doing so much work, you might expect the file size of a variable font to be far larger than a typical font file. Let’s have a (not entirely scientific) look.

Here are some of the variable fonts I have hanging around my laptop, along with their file sizes:

Decovar is only 71 KB even though it has 15 axes

Let’s compare that to single instances of a non-variable version of Source Sans:

Animation

Variable fonts also mean that, for the first time, font-weight (and any other axis) can be animated. While adding type animation may sound like a superfluous embellishment a website can happily survive without, something like adding weight on focus, for example, seems like a natural and intuitive way to denote state to the user. In the past, switching from a normal to a bold weight was utterly jarring. With variable fonts it can be smooth and graceful.

One Size Fits All?

While Lab DJR and Decovar are excitingly creative, variable fonts aren’t all about avant-garde experimentalism. Optical sizing should bring a better reading experience to the web. Currently, type on the web is size agnostic; you can change the font-size and it will still look the same. Optical sizing means making size-specific optimizations for a typeface where the variation of a letter’s form at different sizes can improve readability. We don’t want larger text to look inelegant or clunky, while smaller text benefits from the removal of fine details. More open counters, the thickening of subtle serifs, and an increase in x-height, width, weight and letter-spacing all improve legibility at smaller sizes. The initial value is auto so if you are using a font that makes use of an optical sizing index, you get the benefit for free out of the box.

What Fonts Are Available?

This technology is quickly making its way into browsers. Making use of it requires you to find a variable font you actually want to use. Google Fonts Early Access has three available, with many more likely to follow. Adobe is remaking some of the most well-known families (i.e. Minion, Myriad, Acumin) to be variable. The open source fonts Source Sans and Source Serif have also been released. Monotype, one of the world’s largest typography companies, has so far introduced beta versions of Avenir Next and Kairos Sans. Some independent type foundries have also started to release variable typefaces. With variable font support now available in all major font-creation software, we can expect the availability to greatly expand over 2018.

Using Your Font

Once you’ve found your font, you need to use @font-face to include it on your site.

We don’t want any browsers to download a font they can’t use. For that reason, we should specify the format inside the @font-face rule. Depending on the file type of your variable font, you can specify woff-variations, woff2-variations, opentype-variations or truetype-variations. As already mentioned, you should always use woff2.

@font-face {
  font-family: 'source sans';
  src: url(SourceSansVariable.woff2) format("woff2-variations"),
       url(SourceSans.woff2) format("woff2"); /* for older browsers */
       font-weight: normal; font-style: normal;
}
    
@font-face {
  font-family: 'source sans';
  src: url(SourceSansVariable-italic.woff2) format("woff2-variations"),
       url(SourceSans-italic.woff2) format("woff2");
       font-weight: normal; font-style: italic;
}

A third @font-face is only necessary to provide a backup bold font for browsers that do not support variable fonts. Notice that we are using the same variable font file as for the first @font-face rule, as that file can be both bold and normal:

@font-face {
  font-family: 'source sans';
  src: url(SourceSansVariable.woff2) format("woff2-variations"),
       url(SourceSans-bold.woff2) format("woff2");
       font-weight: 700; font-style: normal;
}

If the browser supports variable fonts, SourceSansVariable.woff2 and SourceSansVariable-italic.woff2 will be downloaded and used. If not, SourceSans.woff2, SourceSans-bold.woff2 and SourceSans-italic.woff2 will be downloaded instead.

From here, we can apply the font on an element as we normally would:

html {
  font-family: 'source sans', Verdana, sans-serif;
}

San Francisco

While variable fonts bring performance benefits, „web-safe” system fonts still remain the most performant option because the font is already installed and there is nothing to download. If you want to use a variable font without the need of downloading anything, Apple’s San Francisco, perhaps the prettiest of all system fonts, is also a variable font. Using system fonts no longer requires a massive font-stack:

html {
  font-family: system-ui, -apple-system;
}

The system-ui value is the new standard to access system fonts, while -apple-system is non-standardized syntax that works on Firefox. Traditionally, system fonts have not come in a wide range of weights or widths. Hopefully more will be made available as variable fonts, bringing all the benefits of variable fonts without a single HTTP request.

Browser Support

Variable fonts have shipped in Chrome and Safari. They are already in the insider preview version of Edge and behind a flag in Firefox. At the current time, not all parts of the spec are fully implemented by Chrome. Using variable fonts in conjunction with font-style, font-stretch, font-weight and font-optical-sizing does not work in Chrome, so using font-variation-settings to control the five standard axes is necessary for the time being. Specifying the format as woff2-variations inside of @font-face also lacks support in Chrome (you can specify only woff2 and the font will still work, but then you are unable to have a non-variable woff2 fallback).


One File, Many Options: Using Variable Fonts on the Web is a post from CSS-Tricks

Boilerform: A Follow-Up

Post pobrano z: Boilerform: A Follow-Up

When Chris wrote his idea for a Boilerform, I had already been thinking about starting a new project. I’d just decided to put my front-end boilerplate to bed, and wanted something new to think about. Chris’ idea struck a chord with me immediately, so I got enthusiastically involved in the comments like an excitable puppy. That excitement led me to go ahead and build out the initial version of Boilerform, which you can check out here.

The reason for my initial excitement was that I have a guilty pleasure for forms. In various jobs, I’ve worked with forms at a pretty intense level and have learned a lot about them. This has ranged from building dynamic form builders to high-level spam protection for a Harley-Davidson® website platform. Each different project has given me a look at the front-end and back-end of the process. Each of these projects has also picked away at my tolerance for quick, lazy implementations of forms, because I’ve seen the drastic implementations of this at scale.

But hey, we’re not bad people. Forms are a nightmare to work with. Although better now: each browser treats them slightly differently. For example, check out these select menus from a selection of browsers and OSs. Not one of them looks the same.

These are just the tip of the inconsistency iceberg.

Because of these inconsistencies, it’s easy to see why developers bail out of digging too deep or just spin up a copy of Bootstrap and be done with it. Also, in my experience, the design of minor forms, such as a contact form are left until later in the project when most of the positive momentum has already gone. I’ve even been guilty of building contact forms a day before a website’s launch. 😬

There’s clearly an opportunity to make the process of working with forms—on the front-end, at least—better and I couldn’t resist the temptation to make it!

The Planning

I sat and thought about what pain-points there are when working with forms and what annoys me as a user of forms. I decided that as a developer, I hate styling forms. As a user, poorly implemented form fields annoy me.

An example of the latter is email fields. Now, if you try to fill in an email field on an iOS device, you get that annoying trait of the first letter being capitalized by the browser, because it treats it like a sentence. All you have to do to stop that behaviour is add autocapitalize="none" to your field and this stops. I know this isn’t commonly known because I rarely see it in place, but it’s such a quick win to have a positive impact on your users.

I wanted to bake these little tricks right into Boilerform to help developers make a user’s life easier. Creating a front-end boilerplate or framework is about so much more than styling and aesthetics. It’s about sharing your gained experience with others to make the landscape better as a whole.

The Specification

I needed to think about what I wanted Boilerform to do as a minimum viable product, at initial launch. I came up with the following rules:

  • It had to be compatible with most front-ends
  • It had to be well documented
  • It had to be lightweight
  • Someone should be able to drop a CDN link to their <head> and have it just work
  • Someone should also be able to expand on the source for their own projects
  • It shouldn’t be too opinionated

To achieve these points, I had some technology decisions to make. I decided to go for a low barrier-to-entry setup. This was:

  • Sass powered CSS
  • BEM
  • Plain ol’ HTML
  • A basic compilation setup

I also focused my attention on samples. CodePen was the natural fit for this because they embed really well. Users can also fork them and play with them themselves.

The last decision was to roll out a pattern library to break up components into little pieces. This helped me in a couple of ways. It helped with organization mainly—but it also helped me build Boilerform in a bitty, sporadic nature as I was working on it in the evenings.

I had my plan and my stack, so got cracking.

Keeping it simple

It’s easy for a project like this to get out of hand, so it’s useful to create some points about what Boilerform will be and also what it won’t be.

What Boilerform will be:

  • It’ll always be a boilerplate to get you off to a good start with your project
  • It’ll provide high-level help with HTML, CSS and JavaScript to make both developers’ and users’ lives easier
  • It’ll aim to be super lightweight, so it doesn’t become a heavy burden
  • It’ll offer configurable options that make it flexible and easy to mould into most web projects

What Boilerform won’t be:

  • It won’t be a silver bullet for your forms—it’ll still need some work
  • It won’t be a framework like Bootstrap or Foundation, because it’ll always be a starting point
  • It won’t be overly opinionated with its CSS and JavaScript
  • It’ll never be aimed at one particular framework or web technology

The Specifics

I know y’all like to dive in to the specifics of how things work, so let me give you a whistle-stop tour!

Namespacing the CSS

The first thing I got sorted was namespacing. I’ve worked on a multitude of different sites and setups and they all share something when it comes to CSS: conflicts. With this in mind, I wrote a @mixin that wrapped all the CSS in a .boilerform namespace.

// Source Sass
.c-button {
  @include namespace() {
    background: gray;
  }
}

// This compiles to this with Sass: 
.boilerform .c-button { background: gray; }

The mixin is basic right now, but it gives us flexibility to scale. If we wanted to make the namespacing optional down-the-line, we only have to update this mixin. I love that sort of modularity.

Right now, what it does give us is safety. Nothing leaks out of Boilerform and hopefully, whatever leaks in will be handled by the namespaced resets and rules.

BEM With a Garnish of Prefixes

I love BEM. It’s been core to my CSS and markup for a few years now. One thing I love about BEM is that it helps you build small, encapsulated components. This is perfect for a project like Boilerform.

I could probably target naked elements safely because of the namespacing, but BEM is about more than just putting classes on everything. It gives me and others the freedom to write whatever markup structure we want. It’s also really easy for someone to pickup the code and understand what’s related to what, in both HTML and CSS.

Another thing I added to this setup was a component prefix. Instead of an .input-field component, we’ve got a .c-input-field component. I hope little things like that will help a new contributor see what’s a component right off the bat.

Horror Inputs Get Some Cool Styling

As mentioned above, select menus are awful to style. So are radio buttons and checkboxes.

A trick I’ve been using for a while now is abstracting the styling to other friendlier HTML elements. For example, with <select> elements, I wrap them in a .c-select-field component and use siblings to add a consistent caret.

For checkboxes and radio buttons, I visually-hide the main input and use adjacent <label> elements to display state change. Using this approach makes working with these controls so much easier. Importantly, we maintain accessibility and native events too.

Base Attributes to Make Fields Easier to Use

I touched on it above with my example about email fields and capitalization, but that wasn’t the only addition of useful attributes.

  • Search fields have autocorrect="off" on them to prevent browsers trying to fix spelling. I strongly recommend that you add this to inputs that a user inserts their name into as well.
  • Number fields have min, max and step attributes set to help with validation. It’s also great for keyboard users.
  • All fields have blank name and id attributes to hopefully speed up the wiring-up process

I’m certainly keen for this to be expanded on, because little tweaks like this are great for user experience.

Going Forward. Can You Help?

Boilerform is in a good place right now, but it has real potential to be useful. Some ideas I’ve had for its ongoing development are:

  • Introducing multiple JavaScript library integrations, such as React, Vue, and Angular
  • Create some base form layouts in the pattern library
  • Create Sass mixins for styling pesky stuff like placeholders
  • Improve configurability
  • Add new elements such as the range input
  • Create multilingual documentation

As you can see, that’s a lot of work, so it would be awesome if we can get some contributors into the project to make something truly useful for our community. Pulling in contributors with different areas of expertise and backgrounds will help us make it useful for as many people as possible, from end-users to back-end developers.

Let’s make something great together. 🙂

Check out the project site or the GitHub repository.


Boilerform: A Follow-Up is a post from CSS-Tricks

People Writing About Style Guides

Post pobrano z: People Writing About Style Guides

Are you thinking about style guides lately? It seems to me it couldn’t be a hotter topic these days. I’m delighted to see it, as someone who was trying to think and build this way when the prevailing wisdom was nice thought, but these never work. I suspect it’s threefold why style guides and design systems have taken off:

  1. Component-based front-end architectures becoming very popular
  2. Styling philosophies that scope styles becoming very popular
  3. A shift in community attitude that style guides work

That last one feels akin to cryptocurrency to me. If everyone believes in the value, it works. If people stop believing in the value, it dies.

Anyway, in my typical Coffee-and-RSS mornings, I’ve come across quite a few recently written articles on style guides, so I figured I’d round them up for your enjoyment.


How to Build a Design System with a Small Team by Naema Baskanderi:

As a small team working on B2B enterprise software, we were diving into creating a design system with limited time, budget and resources … Where do you start when you don’t have enough resources, time or budget?

Her five tips feel about right to me:

  1. Don’t start from scratch
  2. Know what you’re working with (an audit)
  3. Build as you go
  4. Know your limits
  5. Stay organized

Style guide-driven design systems by Brad Frost:

I’ll often have teams stand up the style guide website on Day 1 of their design system initiative. A style guide serves as the storefront that showcases all of the design system’s ingredients and serves as a tangible center of mass for the whole endeavor.


This Also published their style guide (Here’s 100’s of others, if you like peaking at other people’s take on this kind of thing).

What is notable about this to me is that it’s the closest to the meaning of style guide to me (as opposed to a pattern library or design system that are more about design instructions for building out parts of the website). They only include the three things that are most important to their brand: typography, writing, and identity. Smart.

Everything you write should be easy to understand. Clarity of writing usually follows clarity of thought. Take time to think about what you’re going to say, then say it as simply as possible. Keep these rules in mind whenever you’re writing on behalf of the studio.


Laying the foundations for system design by Andrew Couldwell:

I use the term ‘foundations’ as part of a hierarchy for design systems and thinking. Think of the foundations as digital brand guidelines. They inspire and dovetail into our design systems, guiding all our digital products.

  • At a brand level they cover things like values, identity, tone of voice, photography, illustration, colours and typography.
  • At a digital level they cover things like formatting, localization, calls to action, responsive design and accessibility.
  • And in design systems they are the basis of, and cover the application of, things like text styles, form inputs, buttons and responsive grids.

Again a step back and wider view. Yes, a design system, but one that works alongside brand values.


How to create a living style guide by Adriana De La Cuadra:

Similar to a standard style guide, a living style guide provides a set of standards for the use and creation of styles for an application. In the case of a standard style guide, the purpose is to maintain brand cohesiveness and prevent the misuse of graphics and design elements. In the same way LSGs are used to maintain consistency in an application and to guide their implementation. But what makes a LSG different and more powerful is that much of its information comes right from the source code

An easy first reaction might be: Of course our style guide is „living”, we aren’t setting out to build a dead style guide. But I think it’s an interesting distinction to make. Style guides can sit in your development process in different places, as I wrote a few years back.

It’s all to easy to make a style guide that sits on the sidelines or is „the exhaust” of the process. It’s different entirely to place your style guide smack in the middle of a development workflow and not allow any sidestepping.


Lastly, Punit Web rounds up some very recently published style guides, in case you’re particularly interested in fresh ones you perhaps haven’t seen before.


People Writing About Style Guides is a post from CSS-Tricks

Using CSS Clip Path to Create Interactive Effects

Post pobrano z: Using CSS Clip Path to Create Interactive Effects

Do you remember being a kid, cutting out pictures from magazines? Did you glue them onto paper to create your own collages? This post is about cutting out images on the web using the CSS property clip-path. We will discuss how to do the cutting and how we can use these cut-out parts to create some interesting effects, combining these cut-out parts with the original image.

I’ll use the following photo as an example. The flower stands out from the rest of the photo. It is a natural focal point to cut out and create our effects around.

Hand holding a flower
Image courtesy of Unsplash. Credit: Hermes Rivera

Creating the SVG

First off, we are going to create a new SVG file and import our example image into it. You will need image editing software with vector capability to make the cut. I’m using Inkscape, a free open source editor, but you can apply the same method covered in this post using other applications such as Adobe Illustrator or even an online editor, like Vectr.

Let’s start out by creating a new 100px square SVG document in the image editor. It’s important to use a 100px square because clipping paths come in length-percent. Choosing a 0-100 scale will allow for seamless conversion from pixel to percent.

One thing that I find very valuable before proceeding is to examine the SVG code output from the editor. Getting that output depends on the application. For example, there are two methods in Illustrator alone. Looking at the markup gives us insight into what the editor is doing behind the scenes because not all applications export SVG the same way. Seeing the code will increase your understanding of the markup. It’s a win-win.

The SVG output should give us something like this:

<svg … width="100px" height="100px" viewBox="0 0 100 100" …>
	...
</svg>

One of the more important parts of the above is the viewBox attribute because it represents the internal coordinate system of the SVG document. Here’s a thorough explanation of how it works. Above, we can see that the document has the correct proportions where width, height and viewBox all span from 0 to 100.

Next up is importing the image. Here we’ll want to resize the image to 100px square and place it at the origin (0, 0). Doing so might break the aspect ratio of your image, unless your image is already in a square proportion. Our example image is not. This will not be a problem when applying clip-path later on.

Rescaling the image
The image is temporarily rescaled to meet the new square proportion.

Looking at the generated code once again, we now should see the <image> tag inside the SVG document. Note the preserveAspectRatio is set to none. This tells us that the image’s original dimensions are being ignored.

<svg … width="100px" height="100px" viewBox=“0 0 100 100”>
	...
  <image … y="0" x="0" xlink:href=".../image-file-name.jpg" preserveAspectRatio="none" height="100"
width="100" … />
	...
</svg>

Masking the Image File

Now for the actual image cutting.

The concept of cutting images is called masking. If you are unfamiliar with masking it’s essentially drawing a closed shape with the pen tool around an area of the image. You don’t need to be savvy with a vector editor to do this. It doesn’t require any particular artistic skills and it can be done in a few basic steps.

Masking digital images is very much like cutting images out of real magazines. The path created in the vector editor is the same you’d follow using a pair of scissors. Select the pen tool and start drawing the outline of the part of the image that you want to cut out. In this case, it’s the flower focal point we pointed out earlier. Create as many points you like along the way to shape the mask. Be sure to close the path as the last step.

It is important to only use cusp nodes when doing the cutting because clip-path does not support complex shapes, like Bezier curves at the time of this writing. It supports only simple shapes such as polygon, circle and ellipse.

If we view the SVG code now, the output will include a path with all the coordinates of the shape you drew. Here is an abbreviated example of my path’s output:

	...
	<path
		d="m 52.843605,79.860084 -0.69448,1.767767 -0.883884,0 -1.26269,-1.578364 -0.757615,0.06314 -1.388959,-2.714785 -0.12627,-2.967323 -1.704632,2.525381 -1.136422,-0.126269 -0.505076,-2.841054 -1.515229,1.325825 -1.325825,-0.126269 -0.252538,-1.578363 -0.947018,-0.126269 -0.252538,-0.315673 -0.947018,0.126269 -0.69448,-0.757614 0.126269,-1.641498 -0.441942,-0.252538 -0.189403,-2.588516 -0.505077,-0.06314 -1.010152,0.568211 -0.568211,-1.452094 0.441942,-2.399112 -1.325825,-0.126269 -0.378808,-1.262691 0.378808,-2.08344 0.883883,-1.641498 -1.010152,-1.26269 0.505076,-1.957171 -1.452094,-1.010152 -0.378808,-1.010153 1.136422,-2.209709 -2.209709,-0.378807 -0.441941,-1.704632 0.631345,-2.020305 1.704632,-1.38896 -1.578363,-1.452094 0.568211,-2.462247 0.820749,-0.441942 0.126269,-1.515229 0.757614,-1.073287 0.441942,-1.515228 -0.505076,-1.38896 0,-2.272843 0.505076,-1.010153 1.136422,-0.505076 1.325825,0 0.06313,-0.568211 -0.947018,-2.08344 0.378807,-0.631345 0,-0.441942 1.073288,-0.69448 1.073287,0 0.56821,0.315673 -0.189403,-2.525381 0.189403,-0.883884 0.378808,0.757615 0.06313,-0.883884 0.378807,-0.378807 0.189404,-0.378807 0.126269,-2.08344 0.315673,0.06314 0,-0.568211 0.378807,-0.06313 1.199556,0.568211 0.505076,0.69448 0.252538,-2.08344 0.631346,-0.505076 0.631345,-0.568211 0.441942,-0.505076 0.252538,0.505076 0,-0.883883 1.262691,0.315673 0.820749,-1.894036 1.325825,1.136421 1.073287,-1.452094 0.820749,0.189403 1.010152,1.515229 0.505077,0.757615 0.631345,-1.452095 0.820749,-0.56821 0.820749,0.505076 0.378807,0.631345 0.820749,-0.189403 0.820749,0.947018 0,0.252538 0.69448,-0.126269 0.378807,0.631345 0.820749,0 0.568211,1.515229 0.378807,1.325825 0.505076,-0.189404 0.252538,0.441942 0.378808,0.126269 0.441941,2.08344 0,0.568211 0.505077,-0.126269 0,0.883883 0.694479,-0.252538 0.505077,0.505076 0.252538,0.947018 0,0.883884 0.315673,0 0.378807,0.631345 0.441941,0.631345 0.06314,1.515229 -0.378807,1.957171 -0.441942,1.767767 2.904189,-1.136422 0.252538,0.631345 0.126269,2.209709 -0.883884,1.830902 1.38896,0.378807 1.010153,1.199556 -0.378808,1.641498 -0.947018,1.767767 -0.505076,0.378807 0.69448,1.767767 1.010153,1.26269 0.378807,1.38896 -0.378807,1.515229 -0.568211,0.315673 -0.505077,1.010152 -1.452094,0.883884 0.189404,1.325825 0.315672,0.883883 -0.378807,1.38896 -1.388959,1.073287 -0.505077,0.126269 0,0.505077 -0.189403,1.830901 -1.010153,0.631345 0.820749,2.209709 -0.631345,1.452094 -1.641498,-0.189403 0.126269,1.578363 -0.315673,1.641498 -1.073287,0.505076 -0.378807,0.315673 -0.378807,0.883883 -0.252538,1.010153 0.06313,2.714785 -0.631345,0.631345 -1.578364,-0.883883 -0.757614,-1.262691 -0.189404,2.462247 0.189404,2.083439 -0.252538,2.588516 -0.441942,1.894036 -0.631345,0.631346 -0.631345,-0.189404 -0.820749,-0.883883 z"
	/>
	...

Below is a video of me cutting out the flower and the result. The cutting took me about two minutes and the result is pretty decent.

Here is my image with a slight opacity on the mask to show the final shape that was cut out:

Converting SVG to CSS Clip Path

Now that we have the mask let’s have a look at how we go from SVG to clip-path. This means converting the path descriptor, or the d attribute in the SVG code.

Before we look into how to do the conversion, let’s talk a bit about the reasons for using clipping paths. You might ask why we’re creating a clipping path at all? Why not mask the image in the vector editor and export a pre-cut image? It’s possible and using images is a lot more convenient than working with a huge chunks of CSS code. But there are two principal benefits of going with the clipping path as I see it: interactivity and compression. SVG is essentially code in the DOM that can be manipulated and it is a much smaller file size than a bitmap image of the same shape.

The syntax for CSS clipping paths is somewhat the reverse of what it is in SVG. Pairs are comma-separated and spaces separate coordinates. This is the complete opposite to the SVG descriptor syntax. To further complicate the conversion, some shapes only use absolute coordinates. SVG paths are more flexible as they can use both coordinate systems.

I’ve created a rudimentary Node script that converts SVG paths. It takes paths in relative coordinates and outputs corresponding polygons using CSS clip-path. It uses regex to parse SVG files. Feel free to fork it and enhance it. One obvious addition would be to add normalization of proportions. Adding normalization would eradicate the need to only use square images when creating masks.

Here is the result of applying the clip-path to the photo of the flower:

See the Pen Cutting out images by Mikael Ainalem (@ainalem) on CodePen.

Tricks Using CSS Clip Path

Now that we have the cut-out part let’s have a look what we can with it.

Overlay Effect

One quite neat trick is to stack the cut-out part on top of the original image. Below is a pen illustrating the idea of overlaying the cut-out part on top of the original image. It will give you an idea of the positioning and the two different parts involved. Having these two different elements gives us the possibility to apply separate effects on the foreground and the background respectively.

See the Pen Cutting out images #2 by Mikael Ainalem (@ainalem) on CodePen.

Highlighting Effect

Highlighting parts on an image is not just visually appealing. It can also have a real impact on the user experience of your site. It isn’t hard to come up with useful scenarios where you might like to highlight parts of an image in a web page. Highlighting tagged people in a photo could be one use-case. Another could be to highlight certain features of a product in a product showcase. A third case could be a photo of a map, in which you would like to highlight places to do some storytelling around. Highlighting or emphasizing parts of the UI can when properly applied be powerful UX pattern. Using clipping paths is one way to achieve highlights in your UI.

Going back to the photo of the flower we can now easily make the flower stand out. One way to achieve this is to tone down the background behind the flower by lowering its opacity:

See the Pen Cutting out images #3 by Mikael Ainalem (@ainalem) on CodePen.

An interesting note is that the browser will hit-test the mask. Thus, to make it a bit more playful, we can trigger the highlight effect when the user hovers the flower. One way to do this is to add JavaScript event handlers (addEventListener) and attach them to the masked element. Setting these handlers to trigger on events like mouseenter and mouseout captures the user hovering the flower. We can even toggle classes on the background element to trigger the effect. A CSS opacity transition is essentially what’s needed.

See the Pen Cutting out images #4 by Mikael Ainalem (@ainalem) on CodePen.

There is nothing that prevents us from reusing the above technique several times in the same image. Here’s an example of highlighting many people in a photo. In this case there are several overlaid cut-outs:

See the Pen clip-path highlight by Mikael Ainalem (@ainalem) on CodePen.

Fading and Blurring Effects

An effect that we’ve been seeing more in the last year is blurred backgrounds. It’s kind of a reverse way to enhance foreground elements. Rather than enhancing the foreground element itself one can create the same effect by blurring background elements. This way of enhancing foreground elements has another pleasant side-effect: the element in your current focal point remains untouched. But, at the same time, it becomes more prominent.

The easiest way to achieve a blur effect is to use the CSS filter blur. The following pen uses the same JavaScript callback method as the previous example to trigger the effect on hover. Instead of toning down the background, it is blurred with a CSS filter transition.

See the Pen Cutting out images #9 by Mikael Ainalem (@ainalem) on CodePen.

Having a transition using the CSS filter, however, is very costly in terms of performance. This has to do with the shader running on the GPU used to create the blur effect. Animating the CSS blur filter is generally not a good idea. A more performant option is to reuse pre-filtered version of the image and use a cross-fade. In other words, we animate the opacity of a duplicated background image instead of animating the blur. Here is what that looks like:

See the Pen Cutting out images #10 by Mikael Ainalem (@ainalem) on CodePen.

Outline

Another option to reinforce the cut-out element is to use an outline effect. Reusing the mask is an easy way to make it. If we insert the SVG in between the two main elements and add a slight scale (1.04 in this case), we’ll make it appear as an outline.

See the Pen Cutting out images #5 by Mikael Ainalem (@ainalem) on CodePen.

Of course, we can also trigger the outline on hover like we did in the other examples:

See the Pen Cutting out images #6 by Mikael Ainalem (@ainalem) on CodePen.

The edges of the mask are a bit rough as the mask is binary. One option to soften the edges is to add an SVG filter. Here is an example:

See the Pen Cutting out images #8 by Mikael Ainalem (@ainalem) on CodePen.

Cutting Holes

What if the part you want to cut out has holes in it? What if it shows parts of the background that you would like to exclude?

For instance, imagine you want to cut out a donut. Then you want the mask to exclude the hole in the middle. How do you then cut out the mask? The clip-path specification doesn’t allow more than one polygon unless we use SVGs. This means there is no possible way to create more than one shape at a time.

Well, one way to create these holes is to use very thin connectors and draw it as one shape. In other words, we can make a very thin incision from the edge and cut out the hole. This pen illustrates what it looks like when we make these connectors very thin:

See the Pen Cutting out images #7 by Mikael Ainalem (@ainalem) on CodePen.

Morphing Clip Paths

To make the highlight effect even more playful we can actually morph the clip-path itself. Below is an example of creating a dynamic highlight of a butterfly in three shots. The highlight is morphed between the three different cut-out parts on hover.

See the Pen Cutting out images #11 by Mikael Ainalem (@ainalem) on CodePen.

Double Exposure Effect

Another interesting effect we can create with clip-path is a double exposure. Here are two images blend in the same mask.

See the Pen Double exposure by Mikael Ainalem (@ainalem) on CodePen.

Browser support

So, can clip-path be used in all browsers? Unfortunately, not at the moment! If we look the caniuse table it kinda looks a bit like a traffic light at the time of this writing.

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

Desktop

Chrome Opera Firefox IE Edge Safari
66 51 54 No No TP*

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11.0-11.2* 37* No 62 62 57

Wrapping it Up

Some key points I hope you walk away with from this post:

  • Using clipping paths is one way to make parts of your images stand out
  • Stacking the cut-out part on top of the original image makes it possible to create different kinds of highlight effects in images
  • We can leverage the browser’s hit testing on masks to create interactive effects on cut-out parts
  • The clip-path property makes way for UX patterns that highlight and create effects around parts of your images

Acknowledgments

  • Inkscape for vector image editing
  • Unsplash for the royalty-free images used in the examples
  • Github for the repository used for the conversion script
  • npm for being the package mnager for the conversion script
  • caniuse for browser support reference
  • CodePen for hosting the demos used in this post

Using CSS Clip Path to Create Interactive Effects is a post from CSS-Tricks

Secure Contexts Everywhere

Post pobrano z: Secure Contexts Everywhere

Anne van Kesteren for Mozilla says:

Effective immediately, all new features that are web-exposed are to be restricted to secure contexts. Web-exposed means that the feature is observable from a web page or server, whether through JavaScript, CSS, HTTP, media formats, etc. A feature can be anything from an extension of an existing IDL-defined object, a new CSS property, a new HTTP response header, to bigger features such as WebVR. In contrast, a new CSS color keyword would likely not be restricted to secure contexts.

In other words, if your site isn’t HTTPS, you won’t get new web tech features. Holy jeepers. The reasoning is the web should be using HTTPS, so this is our way of beating you with a stick if you try to use fancy features without going HTTPS first.

It’ll be fascinating to watch the first major feature drop and if they stick to their word here. The web dev forums of the internet will overflow with WHY DOESN’T grid-gap WORK WITH MY FLEXBOX? (or some likely coming-soon feature) questions and the answer will be: talk to your server team. What if they drop container queries behind this? That would be a hilarious devastating tornado of developer fury.

Direct Link to Article — Permalink


Secure Contexts Everywhere is a post from CSS-Tricks

2017/2018 JavaScript

Post pobrano z: 2017/2018 JavaScript

There has been a lot of research on the landscape this year! Here are a few snippets from a bunch of articles. There is a ton of information in each, so I’m just picking out a few juicy quotes from each here.

Perhaps the most interesting bit is how different the data looked at is. Each of these is different: a big developer survey, npm data, GitHub data, and StackOverflow data. Yet, they mostly tell the same stories.

The Brutal Lifecycle of JavaScript Frameworks

Ian Allen of StackOverflow writes:

JavaScript UI frameworks and libraries work in cycles. Every six months or so, a new one pops up, claiming that it has revolutionized UI development. Thousands of developers adopt it into their new projects, blog posts are written, Stack Overflow questions are asked and answered, and then a newer (and even more revolutionary) framework pops up to usurp the throne.

Using the Stack Overflow Trends tool and some of our internal traffic data, we decided to take a look at some of the more prominent UI frameworks: Angular, React, Vue.js, Backbone, Knockout, and Ember.

Read More

The Top JavaScript Trends to Watch in 2018

Ryan Chartrand of X-Team for Hackernoon writes:

This time last year, not many had faith that Vue would ever become a big competitor to React when it comes to major companies adopting it, but it was impossible to ignore Vue this year, even sending Angular a bit into the shadows in terms of developer hype.

Read More

The State of JavaScript 2017

Sacha Greif uses a survey rather than usage data:

We asked over a hundred questions to more than 28,000 developers all over the world, covering topics going from front-end libraries all the way to back-end frameworks.

I particularly enjoyed the opinions. Lots of people who love working with JavaScript and find it to be moving in the right direction and find it overly complex.

Read More

The State of JavaScript Frameworks, 2017

This one is from Laurie Voss of npm, which is probably the best source of data for usage but faces interesting challenges with that data:

You can use npm’s download statistics to give you insight into the amount of people actively invested in using and maintaining a package. However, probably more important than absolute popularity is growth.

Packages, once incorporated into software, have very long lives. People very seldom rip packages out of software once they’re installed. Because of this very low “churn,” packages hardly ever decline in usage. Furthermore, nearly all packages in the npm Registry grow in usage as the number of total npm users continues to skyrocket. They vary only in how fast they’re growing.

This makes measuring growth harder, since measuring absolute growth in downloads all the time makes almost everything look popular.

All in all it tells a familiar story: React is incredibly popular and Vue is the one to watch.

Read More

Top JavaScript Libraries & Tech to Learn in 2018

Eric Elliott writes:

Vue.js did do very well in 2017. It got a lot of headlines and a lot of people got interested. As I predicted, it did not come close to unseating React, and I’m confident to predict it won’t unseat React in 2018, either. That said, it could overtake Angular in 2018.

Read More

2017 JavaScript Rising Stars

Michael Rambeau’s writes:

Once again, Vue.js is the trendiest project of the year, with more than 40,000 stars added on GitHub during the year.

It’s far more than in 2016 (26,000 stars), and the gap with the next contender (React) is even bigger.

Read More


2017/2018 JavaScript is a post from CSS-Tricks

Creating Vue.js Component Instances Programmatically

Post pobrano z: Creating Vue.js Component Instances Programmatically

This article assumes basic understanding of Vue.js framework and how to create components in it. If you are new to Vue, then this CSS-Tricks series is a good place to start.

I have been on a Vue.js project that required the ability to create components programmatically. By programmatically, I mean you create and insert the components completely from JavaScript, without writing anything in the template. This article aims to illustrate how different aspects of using components in a template, such as instantiation, props passing, slots, mounting, translate to JavaScript code.

Normally if you are working with the recommended Single File Component style, you would have a Button component like so:

<template>
 <button :class="type"><slot /></button>
</template>
<script>
 export default {
   name: 'Button',
   props: [ 'type' ],
 }
</script>

To use it in another component, all you have to do is import the Button component and insert its tag in the template:

<template>
 <Button type="primary">Click me!</Button>
</template>
<script>
 import Button from 'Button.vue'
 export default {
   name: 'App',
   components: { Button }
 }
</script>

In my case, I don’t know which component to insert in the template and also where to insert it. This information is only available at runtime. So I needed a way to dynamically create component instance for any passed component and insert it in the DOM, during runtime.

Creating the Instance

The very first idea I had to create a dynamic instance of a given component was to pass it to new and it would give me the actual instance. But if you notice carefully the script block in any of the above component code, they are all exporting a simple object, and not a class (constructor function). If I do this:

import Button from 'Button.vue'
var instance = new Button()

…it fails. We need a class. Or, in simple terms, a constructor function. The way to do that is to pass the component object to Vue.extend to create a subclass of the Vue constructor. Now we can create an instance out of it with the new keyword:

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass()

Hooray! Step 1 cleared! Now we need to insert it in the DOM.

Inserting in DOM

Every Vue instance has a method called $mount on it which mounts the component instance on the element you pass to it (i.e. it replaces the passed element with the component instance). This is not a behavior I wanted. I wanted to insert my component instances inside some DOM element. There is a way to do that too. From the official docs:

If elementOrSelector argument is not provided, the template will be rendered as an off-document element, and you will have to use native DOM API to insert it into the document yourself.

That’s what I did:

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass()
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)

There are a couple of things to note in the code above.

First, $refs is the recommended way to get reference to a DOM element in Vue.js. You specify an attribute on the DOM element you want to reference (<div ref="container"></div> in this case) and then that element is available on the set key on component’s $refs property.

Second, to get the native DOM element reference from a Vue component instance, you can use the $el property.

Passing Props to the Instance

Next, I had to pass some props to my Button instance. Specifically, the type prop. The Vue constructor function accepts an options object that we can use to pass in and initialize related things. For passing props, there is a key called propsData which we can use, like so:

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass({
    propsData: { type: 'primary' }
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)

We are almost done, with one final remaining bit. With the normal template method, we used button like: <Button>Click me!</Button>;. We simply wrote the inner text between the tags and it rendered inside the final button tag with the help of slot. But now how do we pass it?

Setting the Slot

If you have used slots in Vue.js, you might be aware that the slots are accessible on any instance on the $slots property. And if named slots are not used, then slots are available on $slots.default as an array. That is the exact key we’ll modify on the instance to set our button’s inner text. Remember, this needs to be done before mounting the instance. Note that in our case we just put a simple string in the slot because that is all we required. But you can pass more complex DOM to it in form for Virtual nodes or VNode using the createElement function. You can read about creating Virtual nodes in the Vue.js documentation. Thanks to a Vue core team member, Rahul, for suggesting this technique.

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass({
    propsData: { type: 'primary' }
})
instance.$slots.default = [ 'Click me!' ]
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)

Final Demo

View Demo

Hope you liked this article. Follow me on Twitter where I share more articles and side projects of mine.


Creating Vue.js Component Instances Programmatically is a post from CSS-Tricks

Meet the New Dialog Element

Post pobrano z: Meet the New Dialog Element

Keith Grant discusses how HTML 5.2 has introduced a peculiar new element: <dialog>. This is an absolutely positioned and horizontally centered modal that appears on top of other content on a page. Keith looks at how to style this new element, the basic opening/closing functionality in JavaScript and, of course, the polyfills that we’ll need to get cross-browser support right.

Also, I had never heard of the ::backdrop pseudo element before. Thankfully the MDN documentation for this pseudo element digs into it a little bit more.

Direct Link to Article — Permalink


Meet the New Dialog Element is a post from CSS-Tricks

Simplifying the Apple Watch Breathe App Animation With CSS Variables

Post pobrano z: Simplifying the Apple Watch Breathe App Animation With CSS Variables

When I saw the original article on how to recreate this animation, my first thought was that it could all be simplified with the use of preprocessors and especialy CSS variables. So let’s dive into it and see how!

Animated gif. Shows the result we want to get: six initially coinciding circles move out from the middle of the screen while the whole assembly scales up and rotates.
The result we want to reproduce.

The structure

We keep the exact same structure.

In order to avoid writing the same thing multiple times, I chose to use a preprocessor.

My choice of preprocessor always depends on what I want to do, as, in a lot of cases, something like Pug offers more flexibility, but other times, Haml or Slim allow me to write the least amount of code, without even having to introduce a loop variable I wouldn’t be needing later anyway.

Until recently, I would have probably used Haml in this case. However, I’m currently partial to another technique that lets me avoid setting the number of items both in the HTML and CSS preprocessor code, which means I avoid having to modify it in both if I need to use a different value at some point.

To better understand what I mean, consider the following Haml and Sass:

- 6.times do
  .item
$n: 6; // number of items

/* set styles depending on $n */

In the example above, if I change the number of items in the Haml code, then I need to also change it in the Sass code, otherwise things break. In a more or less obvious manner, the result is not the intended one anymore.

So we can go around that by setting the number of circles as the value of a CSS variable we later use in the Sass code. And, in this situation, I feel better using Pug:

- var nc = 6; // number of circles

.watch-face(style=`--nc: ${nc}`)
  - for(var i = 0; i < nc; i++)
    .circle(style=`--i: ${i}`)

We’ve also set the index for every .circle element in a similar manner.

The basic styles

We keep the exact same styles on the body, no change there.

Just like for the structure, we use a preprocessor in order to avoid writing almost the same thing multiple times. My choice is Sass because that’s what I’m most comfortable with, but for something simple like this demo, there’s nothing in particular about Sass that makes it the best choice – LESS or Stylus do the job just as well. It’s just faster for me to write Sass code, that’s all.

But what do we use a preprocessor for?

Well, first of all, we use a variable $d for the diameter of the circles, so that if we want to make them bigger or smaller and also control how far out they go during the animation, we only have to change the value of this variable.

In case anyone is wondering why not use CSS variables here, it’s because I prefer to only take this path when I need my variables to be dynamic. This is not the case with the diameter, so why write more and then maybe even have to come up with workarounds for CSS variable bugs we might run into?

$d: 8em;

.circle {
  width: $d; height: $d;
}

Note that we are not setting any dimensions on the wrapper (.watch-face). We don’t need to.

In general, if the purpose of an element is just to be a container for absolutely positioned elements, a container on which we apply group transforms (animated or not) and this container has no visible text content, no backgrounds, no borders, no box shadows… then there’s no need to set explicit dimensions on it.

A side effect of this is that, in order to keep our circles in the middle, we need to give them a negative margin of minus the radius (which is half the diameter).

$d: 8em;
$r: .5*$d;

.circle {
  margin: -$r;
  width: $d; height: $d;
}

We also give them the same border-radius, mix-blend-mode and background as in the original article and we get the following result:

Chrome screenshot. Shows the expected result we get at this point after applying these properties.
The expected result so far (live demo).

Well, we get the above in WebKit browsers and Firefox, as Edge doesn’t yet support mix-blend-mode (though you can vote for implementation and please do that if you want to see it supported because your votes do count), so it shows us something a bit ugly:

Edge screenshot. No mix-blend-mode support means the overlapping regions don't look any different from the non-overlapping ones and the result is uglyish.
The Edge result doesn’t look that good.

To get around this, we use @supports:

.circle {
  /* same styles as before */
  
  @supports not (mix-blend-mode: screen) {
    opacity: .75
  }
}

Not perfect, but much better:

Edge screenshot. Shows the result we get when we use partial transparency to get a result that's more like the mix-blend-mode one in other browsers.
Using @supports and opacity to fix the lack of mix-blend-mode support in Edge (live demo).

Now let’s look a bit at the result we want to get:

Screenshot of the desired circular distribution with annotations. The whole thing is split into two halves (a left one and a right one) by a vertical midline. The first three circles are in the right half and have a bluish green background, while the last three of the six circles are in the left half and have a yellowish green background. The circles are numbered starting from the topmost one in the right half and then they go clockwise.
The desired result.

We have six circles in total, three of them in the left half and three others in the right half. They all have a background that’s some kind of green, those in the left half a bit more towards yellow and those in the right half a bit more towards blue.

If we number our circles starting from the topmost one in the right half and then going clockwise, we have that the first three circles are in the right half and have a bluish green background and the last three are in the left half and have a yellowish green background.

At this point, we’ve set the background for all the circles to be the yellowish blue one. This means we need to override it for the first half of the six circles. Since we cannot use CSS variables in selectors, we do this from the Pug code:

- var nc = 6; // number of circles

style .circle:nth-child(-n + #{.5*nc}) { background: #529ca0 }
.watch-face(style=`--nc: ${nc}`)
  - for(var i = 0; i < nc; i++)
    .circle(style=`--i: ${i}`)

In case you need a refresher on this, :nth-child(-n + a) selects the items at the valid indices we get for n ≥ 0 integer values. In our case, a = .5*nc = .5*6 = 3, so our selector is :nth-child(-n + 3).

If we replace n with 0, we get 3, which is a valid index, so our selector matches the third circle.

If we replace n with 1, we get 2, also a valid index, so our selector matches the second circle.

If we replace n with 2, we get 1, again valid, so our selector matches the first circle.

If we replace n with 3, we get 0, which isn’t a valid index, as indices are not 0-based here. At this point, we stop as it becomes clear we won’t be getting any other positive values if we continue.

The following Pen illustrates how this works – the general rule is that :nth-child(-n + a) selects the first a items:

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

Returning to our circular distribution, the result so far can be seen below:

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

Positioning

First off, we make the wrapper relatively positioned and its .circle children absolutely positioned. Now they all overlap in the middle.

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

In order to understand what we need to do next, let’s take a look at the following illustration:

SVG Illustration. Shows the circles in their initial (overlapping dead in the middle) and final positions (with the rightmost one being highlighted in its final position). The segment between the central point of this circle in the initial position and in the final position is a horizontal segment of length equal to the circle radius.
The rightmost circle going from its initial to its final position (live).

The central points of the circles in the initial position are on the same horizontal line and a radius away from the rightmost circle. This means we can get to this final position by a translation of a radius $r along the x axis.

But what about the other circles? Their central points in the final position are also a radius away from their initial position, only along other lines.

SVG Illustration. Shows the circles in their initial (overlapping dead in the middle) and final positions. The segments connecting the initial position of their central points (all dead in the middle) and the final positions of the same points are highlighted. They're all segments of length equal to the circle radius.
All circles: initial position (dead in the middle) is a radius away from the final one for each and every one of them (live).

This means that, if we first rotate their system of coordinates until their x axis coincides with the line between the initial and final position of the central points and then translate them by a radius, we can get them all in the correct final position in a very similar manner.

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

Alright, but rotate each of them by what angle?

Well, we start from the fact that we have 360° on a circle around a point.

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

We have six circles distributed evenly, so the rotation difference between any two consecutive ones is 360°/6 = 60°. Since we don’t need to rotate the rightmost .circle (the second one), that one’s at 0°, which puts the one before (the first one) at -60°, the one after (the second one) at 60° and so on.

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

Note that -60° and 300° = 360° - 60° occupy the same position on the circle, so whether we get there by a clockwise (positive) rotation of 300° or by going 60° the other way around the circle (which gives us the minus sign) doesn’t matter. We’ll be using the -60° option in the code because it makes it easier to spot a convenient pattern in our case.

So our transforms look like this:

.circle {
  &:nth-child(1 /* = 0 + 1 */) {
    transform: rotate(-60deg /* -1·60° = (0 - 1)·360°/6 */) translate($r);
  }
  &:nth-child(2 /* = 1 + 1 */) {
    transform: rotate(  0deg /*  0·60° = (1 - 1)·360°/6 */) translate($r);
  }
  &:nth-child(3 /* = 2 + 1 */) {
    transform: rotate( 60deg /*  1·60° = (2 - 1)·360°/6 */) translate($r);
  }
  &:nth-child(4 /* = 3 + 1 */) {
    transform: rotate(120deg /*  2·60° = (3 - 1)·360°/6 */) translate($r);
  }
  &:nth-child(5 /* = 4 + 1 */) {
    transform: rotate(180deg /*  3·60° = (4 - 1)·360°/6 */) translate($r);
  }
  &:nth-child(6 /* = 5 + 1 */) {
    transform: rotate(240deg /*  4·60° = (5 - 1)·360°/6 */) translate($r);
  }
}

This gives us the distribution we’ve been after:

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

However, it’s very repetitive code that can easily be compacted. For any of them, the rotation angle can be written as a function of the current index and the total number of items:

.circle {
  /* previous styles */
  
  transform: rotate(calc((var(--i) - 1)*360deg/var(--nc))) translate($r);
}

This works in WebKit browsers and Firefox 57+, but fails in Edge and older Firefox browsers due to the lack of support for using calc() inside rotate() functions.

Fortunately, in this case, we have the option of computing and setting the individual rotation angles in the Pug code and then using them as such in the Sass code:

- var nc = 6, ba = 360/nc;

style .circle:nth-child(-n + #{.5*nc}) { background: #529ca0 }
.watch-face
  - for(var i = 0; i < nc; i++)
    .circle(style=`--ca: ${(i - 1)*ba}deg`)
.circle {
  /* previous styles */
  
  transform: rotate(var(--ca)) translate($r);
}

We didn’t really need the previous custom properties for anything else in this case, so we just got rid of them.

We now have a compact code, cross-browser version of the distribution we’ve been after:

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

Good, this means we’re done with the most important part! Now for the fluff…

Finishing up

We take the transform declaration out of the class and put it inside a set of @keyframes. In the class, we replace it with the no translation case:

.circle {
  /* same as before */
  
  transform: rotate(var(--ca))
}

@keyframes circle {
  to { transform: rotate(var(--ca)) translate($r) }
}

We also add the @keyframes set for the pulsing animation on the .watch-face element.

@keyframes pulse {
  0% { transform: scale(.15) rotate(.5turn) }
}

Note that we don’t need both the 0% (from) and 100% (to) keyframes. Whenever these are missing, their values for the animated properties (just the transform property in our case) are generated from the values we’d have on the animated elements without the animation.

In the circle animation case, that’s rotate(var(--ca)). In the pulse animation case, scale(1) gives us the same matrix as none, which is the default value for transform so we don’t even need to set it on the .watch-face element.

We make the animation-duration a Sass variable, so that, if we ever want to change it, we only need to change it in one place. And finally, we set the animation property on both the .watch-face element and the .circle elements.

$t: 4s;

.watch-face {
  position: relative;
  animation: pulse $t cubic-bezier(.5, 0, .5, 1) infinite alternate
}

.circle {
  /* same as before */
  
  animation: circle $t infinite alternate
}

Note that we’re not setting a timing function for the circle animation. This is ease in the original demo and we don’t set it explicitly because it’s the default value.

And that’s it – we have our animated result!

We could also tweak the translation distance so that it’s not exactly $r, but a slightly smaller value (something like .95*$r for example). This can also make the mix-blend-mode effect a bit more interesting:

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

Bonus: the general case!

The above is for six .circle petals in particular. Now we’ll see how we can adapt it so that it works for any number of petals. Wait, do we need to do more than just change the number of circle elements from the Pug code?

Well, let’s see what happens if we do just that:

Screenshots. They show the result we get for nc equal to 6, 8 and 9. When nc is 6, we have the previous case: splitting the whole thing into two halves with a vertical line, we have the first three (bluish green) circles in the right half and the last three (yellowish green) circles in the left half. When nc is 8, we also have the first half of the circles (the first four, bluish green) on one side of a line splitting the assembly into two geometrically symmetrical halves and the last four circles (yellowish green) on the other side of the same line. This line however isn't vertical anymore. In the nc = 9 case, all circles are yellowish green.
The result for nc equal to 6 (left), 8 (middle) and 9 (right).

The results don’t look bad, but they don’t fully follow the same pattern – having the first half of the circles (the bluish green ones) on the right side of a vertical symmetry line and the second half (yellowish green) on the left side.

We’re pretty close in the nc = 8 case, but the symmetry line isn’t vertical. In the nc = 9 case however, all our circles have a yellowish green background.

So let’s see why these things happen and how we can get the results we actually want.

Making :nth-child() work for us

First off, remember we’re making half the number of circles have a bluish green background with this little bit of code:

.circle:nth-child(-n + #{.5*nc}) { background: #529ca0 }

But in the nc = 9 case, we have that .5*nc = .5*9 = 4.5, which makes our selector :nth-child(-n + 4.5). Since 4.5 is not an integer, the selector isn’t valid and the background doesn’t get applied. So the first thing we do here is floor the .5*nc value:

style .circle:nth-child(-n + #{~~(.5*nc)}) { background: #529ca0 }

This is better, as for a nc value of 9, the selector we get is .circle:nth-child(-n + 4), which gets us the first 4 items to apply a bluish green background on them:

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

However, we still don’t have the same number of bluish green and yellowish green circles if nc is odd. In order to fix that, we make the circle in the middle (going from the first to the last) have a gradient background.

By „the circle in the middle” we mean the circle that’s an equal number of circles away from both the start and the end. The following interactive demo illustrates this, as well as the fact that, when the total number of circles is even, we don’t have a middle circle.

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

Alright, how do we get this circle?

Mathematically, this is the intersection between the set containing the first ceil(.5*nc) items and the set containing all but the first floor(.5*nc) items. If nc is even, then floor(.5*nc) and ceil(.5*nc) are equal and our intersection is the empty set ∅. This is illustrated by the following Pen:

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

We get the first ceil(.5*nc) items using :nth-child(-n + #{Math.ceil(.5*nc)}), but what about the other set?

In general, :nth-child(n + a) selects all but the first a - 1 items:

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

So in order to get all but the first floor(.5*nc) items, we use :nth-child(n + #{~~(.5*nc) + 1}).

This means we have the following selector for the middle circle:

:nth-child(n + #{~~(.5*nc) + 1}):nth-child(-n + #{Math.ceil(.5*nc)})

Let’s see what this gives us.

  • If we have 3 items, our selector is :nth-child(n + 2):nth-child(-n + 2), which gets us the second item (the intersection between the {2, 3, 4, ...} and {2, 1} sets)
  • If we have 4 items, our selector is :nth-child(n + 3):nth-child(-n + 2), which doesn’t catch anything (the intersection between the {3, 4, 5, ...} and {2, 1} sets is the empty set ∅)
  • If we have 5 items, our selector is :nth-child(n + 3):nth-child(-n + 3), which gets us the third item (the intersection between the {3, 4, 5, ...} and {3, 2, 1} sets)
  • If we have 6 items, our selector is :nth-child(n + 4):nth-child(-n + 3), which doesn’t catch anything (the intersection between the {4, 5, 6, ...} and {3, 2, 1} sets is the empty set ∅)
  • If we have 7 items, our selector is :nth-child(n + 4):nth-child(-n + 4), which gets us the fourth item (the intersection between the {4, 5, 6, ...} and {4, 3, 2, 1} sets)
  • If we have 8 items, our selector is :nth-child(n + 5):nth-child(-n + 4), which doesn’t catch anything (the intersection between the {5, 6, 7, ...} and {4, 3, 2, 1} sets is the empty set ∅)
  • If we have 9 items, our selector is :nth-child(n + 5):nth-child(-n + 5), which gets us the fifth item (the intersection between the {5, 6, 7, ...} and {5, 4, 3, 2, 1} sets)

Now that we can select the item in the middle when we have an odd number of them in total, let’s give it a gradient background:

- var nc = 6, ba = 360/nc;

style .circle:nth-child(-n + #{~~(.5*nc)}) { background: var(--c0) }
  | .circle:nth-child(n + #{~~(.5*nc) + 1}):nth-child(-n + #{Math.ceil(.5*nc)}) {
  |   background: linear-gradient(var(--c0), var(--c1))
  | }
.watch-face(style=`--c0: #529ca0; --c1: #61bea2`)
  - for(var i = 0; i < nc; i++)
    .circle(style=`--ca: ${(i - 1)*ba}deg`)

The reason why we use a top to bottom gradient is that, ultimately, we want this item to be at the bottom, split into two halves by the vertical symmetry line of the assembly. This means we first need to rotate it until its x axis points down and then translate it down along this new direction of its x axis. In this position, the top of the item is in the right half of the assembly and the bottom of the item is in the left half of the assembly. So, if we want a gradient from the right side of the assembly to the left side of the assembly, this is a top to bottom gradient on that actual .circle element.

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

Using this technique, we have now solved the issue of the backgrounds for the general case:

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

Now all that’s left to do is make the symmetry axis vertical.

Taming the angles

In order to see what we need to do here, let’s focus on the desired positioning in the top part. There, we want to always have two circles (the first in DOM order on the right and the last in DOM order on the left) symmetrically positioned with respect to the vertical axis that splits our assembly into two halves that mirror each other.

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

The fact that they’re symmetrical means the vertical axis splits the angular distance between them ba (which is 360° divided by the total number of circles nc) into two equal halves.

SVG Illustration. Shows the circles distributed around a central point with the two at the top (symmetrical with respect to the vertical axis that splits the whole assembly into two mirrored halves) being highlighted. This middle axis splits the angular distance between the central points of these two circles into two equal halves.
Angles formed by vertical symmetry line and the radial lines to the central points of the top angles are both equal to half a base angle (live).

So both are half a base angle (where the base angle ba is 360° divided by the total number of circles nc) away from the vertical symmetry axis, one in the clockwise direction and the other one the other way.

The upper half of the symmetry axis is at -90° (which is equivalent to 270°).

SVG Illustration. Shows degrees around a circle in 90° steps. We start from the right (3 o'clock). This is the 0° and, in general, any multiple of 360° angle. Going clockwise, we have 90° down (at 6 o'clock), 180° on the left (at 9 o'clock) and 270° at the top (12 o'clock). Going the other way from 0°, we have -90° at the top (12 o'clock), -180° on the left (9 o'clock) and so on.
Degree values around the circle (live).

So in order to get to the first circle in DOM order (the one at the top on the right), we start from 0°, go by 90° in the negative direction and then by half a base angle back in the positive direction (clockwise). This puts the first circle at .5*ba - 90 degrees.

SVG Illustration. Shows graphically how to get the angular position of the first circle. Starting from 0° (3 o'clock), we go in the negative direction by 90° (getting at 12 o'clock). Afterwards, we go back in the positive direction by half a base angle.
How to get the angle the first circle is placed at (live).

After that, every other circle is at the angle of the previous circle plus a base angle. This way, we have:

  • the first circle (index 0, selector :nth-child(1)) is at ca₀ = .5*ba - 90 degrees
  • the second circle (index 1, selector :nth-child(2)) is at ca₁ = ca₀ + ba = ca₀ + 1*ba degrees
  • the third circle (index 2, selector :nth-child(3)u) is at ca₂ = ca₁ + ba = ca₀ + ba + ba = ca₀ + 2*ba degrees
  • in general, the circle of index k is at caₖ = caₖ₋₁ + ba = ca₀ + k*ba degrees

So the the current angle of the circle at index i is .5*ba - 90 + i*ba = (i + .5)*ba - 90 degrees:

- var nc = 6, ba = 360/nc;

//- same as before
.watch-face(style=`--c0: #529ca0; --c1: #61bea2`)
  - for(var i = 0; i < nc; i++)
    .circle(style=`--ca: ${(i + .5)*ba - 90}deg`)

This gives our final Pen, where we only need to change nc from the Pug code to change the result:

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


Simplifying the Apple Watch Breathe App Animation With CSS Variables is a post from CSS-Tricks