Month: January 2022

Notes on Reverse-Scrolling Columns With CSS Scroll-Timeline

Lemme do this one quick-hits style:

Showing three columns of photos of actresses demonstrating the CSS scroll-timeline effect. The first and their columns scroll up and down together. The second columns scrolls the opposite direction.

CSS Scroll-Timeline with prefers-reduced-motion

The only thing I’d add is something to honor prefers-reduced-motion, as I could see this sort of scrolling motion affecting someone with motion sickness. To do that, you could combine tests in the same line the support test is being done in JavaScript:

if (     !CSS.supports("animation-timeline: foo") &&      !window.matchMedia('(prefers-reduced-motion: reduce)').matches    ) {      // Do fancy stuff }

I’m not 100% if it’s best to test for no-preference or the opposite of reduce. Either way, the trick in CSS is to wrap anything you’re going to do with @scroll-timeline and animation-timeline in an @supports test (in case you want to do something different otherwise) and then wrap that in a preference test:

@media (prefers-reduced-motion: no-preference) {      @supports (animation-timeline: works) {        /* Do fancy stuff */      }  }

Here’s a demo of that, with all the real credit to Bramus here for getting it going.

Ooo and ya know what? The CSS gets nicer should @when land as a feature:

@when supports(animation-timeline: works) and media(prefers-reduced-motion: no-preference) {  } @else {  }

Notes on Reverse-Scrolling Columns With CSS Scroll-Timeline originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , ,

The Relevance of TypeScript in 2022

It’s 2022. And the current relevance of TypeScript is undisputed. TypeScript has dominated the front-end developer experience by many, many accounts. By now you likely already know that TypeScript is a superset of JavaScript, building on JavaScript by adding syntax for type declarations, classes, and other object-oriented features with type-checking.

And when I say dominated, I mean TypeScript has literally exploded on the scene since it was introduced in 2012.

The relevance of TypeScript illustrated in a line chart showing the popularity of multiple languages since 2014. TypeScript's popularity has risen the fastest among 10 popular languages and ranks fourth overall.
Source: State of the Octoverse 2021 (GitHub)

That sort of growth is incredible, especially considering it really started taking off in 2017. But as we get into 2022, just how relevant is TypeScript going to be moving forward? It’s not like TypeScript will continue to grow leaps and bounds this way forever… right?!

It’s interesting to poke at the idea a bit to see where TypeScript is today and how it will continue playing a role in front-end development into the future. Jake Albaugh has already poked at the relevance of TypeScript himself, but from the perspective of whether or not knowing JavaScript makes you relevant as a developer.

So, what’s the future relevance of TypeScript look like? Let’s see.

TypeScript’s roots

OK, so we know TypeScript adds syntax to JavaScript. This syntax is used by TypeScript’s compiler to sniff out code errors before they happen, then it spits out vanilla JavaScript that browsers can understand. It’s also worth mentioned that TypeScript is maintained by Microsoft, licensed under Apache 2 license.

A blue oval labeled TypeScript code with an arrow to the right pointing at a green oval labeled TypeScript compiler, followed by another arrow pointing right toward a red oval labeled JavaScript code.

And we can’t really talk about TypeScript without also calling out ECMAScript (ES), the JavaScript standard and scripting language specification standardized by ECMA International. The JavaScript naming convention started with ES1 and has evolved to ES6. The most recent version, the 12th edition — or ECMAScript 2021 — was published in June 2021.

TypeScript is a strict superset of ECMAScript 2015. That means a JavaScript program is also a valid TypeScript program. Conversely, a TypeScript program can effortlessly consume JavaScript.

Concentric circles in blue, orange, green, and magenta showing how TypeScript encompasses ES 2016, ES 2015, and ES 5.
Credit: Seema Saharan

It’s important to know all this because we need to know where TypeScript gets its roots in order to poke at its possible future.

TypeScript’s components

There are three fundamental components of TypeScript that make it as awesome as it is. Not only do we get the aforementioned type-checking that comes with the TypeScript language, but we get the TypeScript compiler and language service as well.

A tree chart showing TypeScript at the top with three branches representing its language, compiler, and language service. Each of those has a single branch explaining what those components do.

These are the pieces that keep TypeScript relevant, so to speak. The language is what developers love writing. The compiler is what interprets the language for browsers. The service processes the language on demand with blazing speed. Without these, TypeScript just ain’t what it is.

TypeScript support

There’s another key piece to TypeScript’s relevance that often goes overlooked: it’s super well-supported by text editors. TypeScript’s relevance is only as good as it is accessible and something that can be picked up by just about any front-ender.

TypeScript was initially supported only in Microsoft’s Visual Studio code editor. Makes sense, right? I mean, TypeScript is maintained by Microsoft and all. But as TypeScript grew legs, more code editors and IDEs began started supporting it either natively or with plugins.

Some of the most popular editors and IDEs, besides Visual Studio Code, include:

And with more support comes more TypeScript relevance. The fact that you can pick up nearly any code editor and start hammering out TypeScript code makes it more and more a go-to choice as it’s simply available where you want it.

TypeScript’s evolution

From its initial release in 2012 to the present day (early 2022), there have been many improvements released in each version of TypeScript, like:

  • TypeScript 1.6 introduced the .tsx file extension, which enabled JSX within TypeScript files and made the new as operator the default way to cast.
  • TypeScript 2 brought in a major improvement by allowing developers to optionally prevent variables from being assigned null values.
  • Version 2.3 of TypeScript introduced support for ES6 features, such as generators and iterators.
  • TypeScript 3 brought in language enhancements, such as tuples in REST parameters and spread expressions.
  • TypeScript 4 (we’re currently at 4.5.2 at the time of this writing) continues the evolution with refinements to tuples, template literal types, smarter type alias preservation, and improvements to Awaited and Promise.

This is exactly the sort of speed at which you might expect to see a blossoming programming language iterating and releasing new features. Again, good context when evaluating the relevance of TypeScript moving forward.

TypeScript’s popularity

We’ve already established that TypeScript is, like, super popular. The chart that kicked off this post showed TypeScript growing at breakneck speed in a matter of a few years to rank as the fourth most popular language. But don’t just take my word and GitHub’s word for it (it is owned by Microsoft after all). Here’s a bunch of published research from various places saying the same thing.

RedMonk

RedMonk, a development industry analysis firm has this to say about ranking TypeScript eighth in its 2021 list of most popular languages:

Does [TypeScript] have the capacity to move up and outperform long term incumbents such as C#, C++ or even PHP eventually, or is TypeScript essentially at or near the limits of its potential? It’s impossible to say with any reliability, but it is interesting to note that a year ago at this time TypeScript lagged the fifth place languages by six points in the combined score that the rankings are based on, but in this run the gap was only two points. Past performance doesn’t always predict future performance, of course, but it suggests at least that TypeScript might yet have some room in front of it.

PYPL Index

The PYPL Index is a measure of Google searches for programming language tutorials. It’s not exact science, but a good indicator of interest. And, over time, TypeScript appears to be trending in a flat direction. TypeScript currently ranks eighth and, compared to a year ago at this time, PYPL indicates that TyeScript is trending flat overall while other languages, like Python and C++ are trending up year-over-year.

Stack Overflow 2021 Developer Survey

According to Stack Overflow’s 2021 Developer Survey, TypeScript is about as popular as PYPL indicates it is, coming in as the seventh most popular language, as ranked by approximately 83,000 developers.

An orange bar chart on a dark background showing TypeScript ranking seventh behind the likes of JavaScript, HTML/CSS, Python, SWL, Java, and Node.

The Stack Overflow annual survey is one of the most credible and most-awaited developer surveys. It uses a humongous developer base from all over the world to arrive at its conclusions. And how relevant does this say TypeScript is in the front-end community? Well, it’s not only the seventh most popular language, but it the second technology that developers want to work with the most (followed by Python), and the third most loved language (behind Rust and Clojure).

Source: Stack Overflow Developer Survey 2021

2020 State of JavaScript

This annual survey (the next one is open now!) shows that TypeScript boasts a sparkling 93% satisfaction rate (up from 89% in 2019) among developers which is tops in the rankings. It also took top prize in interest (70%, up from 66%), usage (78%, up from 66%), and awareness (100% which is shockingly flat from 2019).

Screenshot of the 2021 State of JavaScript survey chart of popular JavaScript flavors. TypeScript was second in 2016 but took first place in 2017 and has remained there since, above PostScript, Reason, Elm, and ClojureScript.

GitHut 2.0 Language Rankings

This ranking is an analysis that interacts with GitHub to suss out the most used languages across GitHub. And it’s indicative of TypeScript’s relevance in that TypeScript ranked seventh in the first quarter of 2021 before leaping up to fourth in the fourth quarter, and with the highest year-over-year change.

OK, so it’s clear that TypeScript is a big deal. But again, how relevant will it be moving forward?

The relevance of TypeScript in 2022 and beyond

So far, I’ve tried to paint a picture that identifies where TypeScript fits into the front-end development landscape, showing how it’s quickly evolved into a mature and serious contender as a programming language, and is fast-becoming both the programming language of choice and the one people like most.

In other words: TypeScript is relevant today.

But if we want to take a guess at where TypeScript’s current success is taking it, then it’s worth taking a peek the official TypeScript roadmap over at GitHub.

Here’s what we have to look forward to:

  • typeof class changes
  • Allow more code before super calls in subclasses
  • Generalized index signatures
  • --noImplicitOverride and the override keyword
  • Static index signatures
  • Use unknown as the type for catch clause variables
  • Investigate nominal typing support
  • Flattening declarations
  • Implement the ES decorator proposal
  • Investigate ambient, deprecated, and conditional decorators
  • Investigate partial type argument inference
  • Implement a quick fix to scaffold local @types packages
  • Investigate error messages in haiku or iambic pentameter
  • Implement decorators for function expressions and arrow functions

I think all of these roadmapped features are both exciting and will play a big role in maintaining the relevance of TypeScript for the foreseeable future. And while I think all of them are worthy of deeper discussion, here are a few I believe are core for TypeScript in 2022 and beyond.

Flattening declarations

The flattening declarations proposal, for example, aims to enable bundling declarations for TypeScript projects so that a library can be consumed with a single TypeScript file, regardless of how many modules it may contain internally.

The idea with flattening declarations is that a single amalgamated and flattened .d.ts file, in addition to a single output .js file, should be emitted by the TypeScript compiler. Access modifiers should be taken into consideration and respected when generating the DTS. Having a single declaration file with flattened declarations will make things much easier for developers and improve maintainability in the long run.

Ambient, Deprecated, and Conditional decorators

Design time decorators— such as ambient and conditional decorators — are another feature to look forward to. Decorators enable developers to add both annotations and metadata to existing code in a declarative way. In TypeScript, each decorator has a special name starting with @ that will not be emitted in the converted JavaScript, but can be persisted in .d.ts outputs.

Consider, for example, if you could issue a warning whenever someone attempts to employ a deprecated method or property so that they could upgrade to a newer library version. By having ambient, deprecated, and conditional decorators as part of the TypeScript specification in the future, the language will provide more powerful ways for developers to annotate their code and include metadata in it.

Decorators for function expressions/arrow functions

Decorators for function and arrow expressions is another feature I think will build on TypeScript’s ongoing relevance. Adding annotations or metadata to those expressions will enable developers to determine at runtime information about which the decorator has been applied.

Investigate error messages in haiku or iambic pentameter

OK, so maybe this one isn’t so much about the relevance of TypeScript’s robust feature set, but I think the personality it adds to the language is part of the overall package that makes TypeScript a pleasure to use. How cool (and pleasant) would it be to get an error message like this:

Sure beats a programmatic message that can sometimes feel like a scolding! And while there has been no progress on this proposed feature in the last two years, it still exists on the official roadmap which means someone will eventually work on it.


Microsoft unveiled Visual Studio 2022 Preview 3 back in August 2021. There was a lot to get excited about with that release, like new JavaScript and TypeScript tools to enhance the experience for single-page applications and front-end development. Plus, it included a new JavaScript/TypeScript project type to facilitate developers building standalone Angular, React, and Vue projects. Then there’s the enhancement that Visual Studio will leverage the native CLIs of each JavaScript framework to build front-end project templates.

All of this to say that TypeScript is not just evolving; it is exploding and only gaining momentum as we settle in 2022. So, yes, TypeScript is relevant in 2022… and will continue to be for some time to come.


The Relevance of TypeScript in 2022 originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, ,
[Top]

How to Promote and Sell Your Art on Pinterest?

[Top]

The CSS from-font Value Explained in 4 Demos

I was doing my Advent of UI Components, and I stumbled upon the from-font value for the text-decoration-thickness CSS property. I was curious about it, so I did a little research and I think what I found (and learned) is both interesting and worth sharing.

About the from-font value

Here’s how MDN defines the from-font value:

If the font file includes information about a preferred thickness, use that value. If the font file doesn’t include this information, behave as if auto was set, with the browser choosing an appropriate thickness.

So, the from-font value is used only if the font file has the definition for the thickness of the line. Otherwise, browsers use the auto value, which tells the browser to choose the thickness. I wanted to find out how that works, so I made a few demos comparing it to the other values.

Demo 1: text-decoration-thickness: auto

In the first demo, I wanted to see how the auto value for thickness works with under, over, and strikethrough lines for the default font family.

I didn’t find anything particularly interesting here, except that some combinations don’t work very well for strikethrough text (if you ask me). For example, using a wavy decoration with strikethrough isn’t readable, but that might be the desired output in some scenarios, I guess.

Demo 2: text-decoration-thickness: 0px

In the second demo, I wanted to see how the text works with thin lines.

The lines work with paragraphs or smaller text, but the thin strikethrough line doesn’t work very well with large text as the strikethrough line is hard to detect.

Showing the from-font value on larger text. The text is black and the line through the text is thin and barely noticeable.

I also learned that you cannot set the line thickness below 1px. In the demo, the line thickness is set to 0px, but the browser renders a 1px line anyway.

Demo 3: text-decoration-thickness: from-font and font-weight

Next, I wanted to see if the text-decoration-thickness: from-font declaration changes with the font weight. On the left, the value is set to from-font; on the right, the value is set to auto.

The from-font value doesn’t seem to follow changes to the text’s font weight, at least not with when Roboto is the font family. There is no difference between how big or bold the text is set. The line thickness is the same if the value is set to from-font.

It is worth noting that Firefox renders the line thickness the same for both values, so my guess is that Firefox actually uses the from-font value for the auto value.

Demo 4: text-decoration-thickness: from-font and font-family

In this final demo, I wanted to see how the from-font value works with different font families. It doesn’t impact the paragraphs or the smaller font sizes because it renders the smallest value, 1px. The difference is visible for the bigger font sizes, like default <h1> elements, but only if you look very closely. Also, the strikethrough line is once again too thin on bigger text. This is something that font designers and developers might consider when designing and defining fonts.

Browser support

You can most certainly use the text-decoration-thickness property today since most modern browsers support this property.

So, should you use it?

Although the from-font value might seem like a good idea, I don’t think it should be used just yet. There are too many inconsistencies with the default text-decoration-thickness value across the browsers (which [Šime Vidas has] covered](https://css-tricks.com/css-underlines-are-too-thin-and-too-low-in-chrome) in great depth), so it is no surprise that the from-font value is still not working that well. Maybe the from-font value should be defined in percentages or some other relative unit so that it changes with the font size. Maybe font designers feel that it shouldn’t work that way. Either way, it seems like more discussion is warranted to nail down the property value’s default behavior and how it renders.

I am using the from-font value on my personal site for the link underlines in the articles, and I think it works great. The line is subtle, but it still communicates the interaction.

I look forward to seeing more options for the text-decoration-thickness in the future.


The CSS from-font Value Explained in 4 Demos originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , ,
[Top]

Git: Switching Unstaged Changes to a New Branch

I’m always on the wrong branch. I’m either on master or main working on something that should be on a fix or feature branch. Or I’m on the last branch I was working on and should have cut a new branch. Oh well. It’s never that big of a deal. Basically means switching unstaged changes to a new branch. This is what I normally do:

  • Stash all the changed-but-unstaged files
  • Move back to master
  • Pull master to make sure it’s up to date
  • Cut a new branch from master
  • Move to the new branch
  • Unstash those changed files

Want a bunch of other Git tips? Our “Advanced Git” series has got a ton of them.

Switching unstaged changes to a new branch with the Git CLI it looks like this

Here’s how I generally switch unstaged changes to a new branch in Git:

git status git stash --include-untracked git checkout master git pull git branch content/sharis git checkout content/sharis git stash pop
Yeah I commit jpgs right to git.

Switching unstaged changes to a new branch in Git Tower it looks like this

I think you could theoretically do each of those steps to switch unstaged changed to a new branch, one-by-one, in Git Tower, too, but the shortcut is that you can make the branch and double-click over to it.

Sorry, I’m just doing Git Tower but there are lots of other Git GUIs that probably have clever ways of doing this as well.

But there is a new fancy way!

This way of switching unstaged changes to a new branch is new to me anyway, and it was new to Wes when he tweeted this:

Cool. That’s:

git switch -c new-branch

Documentation for that here.


Git: Switching Unstaged Changes to a New Branch originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , ,
[Top]

Build, Ship, & Maintain Design Systems with Backlight

(This is a sponsored post.)

Design systems are an entire job these days. Agencies are hired to create them. In-house teams are formed to handle them, shipping them so that other teams can use them and helping ensure they do. Design systems aren’t a fad, they are a positive evolution of how digital design is done. Backlight is the ultimate all-in-one development tool for design systems.

I think it’s interesting to start thinking about this at the end. What’s the best-case scenario for a design system for websites? I think it’s when you’ve published a versioned design system to npm. That way teams can pull it in as a dependency on the project and use it. How do you do that? Your design system is on GitHub and you publish from there. How do you do that? You work on your design system through a development environment that pushes to GitHub. What is Backlight? It’s that development environment.

Spin up a complete design system in seconds

Wanna watch me do it?

You don’t have to pick a starter template, but it’s enlightening to see all the possibilities. Backlight isn’t particularly opinionated about what technology you want to use for the system. Lit and Web Components? Great. React and Emotion? Cool. Just Vue? All good. Nunjucks and Sass? That works.

Having a starter design system really gives you a leg up here. If you’re cool with using something off-the-shelf and then customizing it, you’ll be off and running incredibly quickly. Something that you might assume would take a few weeks to figure out and settle into is done in an instant. And if you want to be 100% custom about everything, that’s still completely on the table.

Kick it up to GitHub

Even if you’re still just testing, I think it’s amazingly easy and impressive how you can just create a GitHub (or GitLab) repo and push to it in a few clicks.

To me, this is the moment it really becomes real. This isn’t some third-party tool where everyone is 100% forced to use it and you’re locked into it forever and it’s only really useful when people buy into the third-party tool. Backlight just takes very industry-standard practices and makes them easier and more convenient to work with.

Then, kick it to a registry.

Like I said at the top, this is the big moment for any design system. When you send it to a package registry like npm or GitHub packages, that means that anyone hoping to use your design system can now install it and use it like any other dependency.

In Backlight, this is just a matter of clicking a few buttons.

With a PRO membership, you can change the scope to your own organization. Soon you’ll be handling all your design system releases right from here, including major, minor, and patch versions.

Make a Component

I’d never used Backlight before, nobody helped me, and I didn’t read any of the (robust) documentation. I just clicked around and created a new Component easily. In my case here, I made a new Nunjucks macro, made some SCSS styles, then created a demo of it as a Storybook “story”. All I did was reference an existing component to see how it all worked.

As one of the creators of CodePen, of course, I highly appreciated the in-browser IDE qualities to all this. It runs re-builds your code changes (looks like a Vite process) super quickly, alerting you helpfully to any errors.

Now because this is a Very Real Serious Design System, I wouldn’t push this new component directly to master in our repository, first it becomes a branch, and then I commit to that. I wouldn’t have to know anything at all about Git to pull this off, look how easy it is:

Howdy, Stakeholders!

Design systems are as much of a people concern as they are a technological concern. Design systems need to get talked about. I really appreciate how I can share Backlight with anyone, even if they aren’t logged in. Just copy a sharing link (that nobody could ever guess) and away you go.

There is a lot here.

You can manage an entire design system in here. You’re managing things from the atomic token level all the way up to building example pages and piecing together the system. You’re literally writing the code to build all this stuff, including the templates, stories, and tests, right there in Backlight.

What about those people on your team who really just can’t be persuaded to leave their local development environment. Backlight understands this, and it doesn’t force them to! Backlight has a CLI which enables local development, including spinning up a server to preview active work.

But it doesn’t stop there. You can build documentation for everything right in Backlight. Design systems are often best explained in words! And design systems might actually start life (or live a parallel life) in entirely design-focused software like Figma, Sketch, or Adobe XD. It’s possible to link design documents right in Backlight, making them easy to find and much more organized.


I’m highly impressed! I wasn’t sure at first what to make of a tool that wants to be a complete tool for design systems, knowing how complex that whole world is, but Backlight really delivers in a way that I find highly satisfying, especially coming at it from the role of a front-end developer, designer, and manager.


Build, Ship, & Maintain Design Systems with Backlight originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , , , ,
[Top]

Demystifying TypeScript Discriminated Unions

TypeScript is a wonderful tool for writing JavaScript that scales. It’s more or less the de facto standard for the web when it comes to large JavaScript projects. As outstanding as it is, there are some tricky pieces for the unaccustomed. One such area is TypeScript discriminated unions.

Specifically, given this code:

interface Cat {   weight: number;   whiskers: number; } interface Dog {   weight: number;   friendly: boolean; } let animal: Dog | Cat;

…many developers are surprised (and maybe even angry) to discover that when they do animal., only the weight property is valid, and not whiskers or friendly. By the end of this post, this will make perfect sense.

Before we dive in, let’s do a quick (and necessary) review of structural typing, and how it differs from nominal typing. This will set up our discussion of TypeScript’s discriminated unions nicely.

Structural typing

The best way to introduce structural typing is to compare it to what it’s not. Most typed languages you’ve probably used are nominally typed. Consider this C# code (Java or C++ would look similar):

class Foo {   public int x; } class Blah {   public int x; }

Even though Foo and Blah are structured exactly the same, they cannot be assigned to one another. The following code:

Blah b = new Foo();

…generates this error:

Cannot implicitly convert type 'Foo' to 'Blah'

The structure of these classes is irrelevant. A variable of type Foo can only be assigned to instances of the Foo class (or subclasses thereof).

TypeScript operates the opposite way. TypeScript considers types to be compatible if they have the same structure—hence the name, structural typing. Get it?

So, the following runs without error:

class Foo {   x: number = 0; } class Blah {   x: number = 0; } let f: Foo = new Blah(); let b: Blah = new Foo();

Types as sets of matching values

Let’s hammer this home. Given this code:

class Foo {   x: number = 0; }  let f: Foo;

f is a variable holding any object that matches the structure of instances created by the Foo class which, in this case, means an x property that represents a number. That means even a plain JavaScript object will be accepted.

let f: Foo; f = {   x: 0 }

Unions

Thanks for sticking with me so far. Let’s get back to the code from the beginning:

interface Cat {   weight: number;   whiskers: number; } interface Dog {   weight: number;   friendly: boolean; }

We know that this:

let animal: Dog;

…makes animal any object that has the same structure as the Dog interface. So what does the following mean?

let animal: Dog | Cat;

This types animal as any object that matches the Dog interface, or any object that matches the Cat interface.

So why does animal—as it exists now—only allow us to access the weight property? To put it simply, it’s because TypeScript does not know which type it is. TypeScript knows that animal has to be either a Dog or Cat, but it could be either (or both at the same time, but let’s keep it simple). We’d likely get runtime errors if we were allowed to access the friendly property, but the instance wound up being a Cat instead of a Dog. Likewise for the whiskers property if the object wound up being a Dog.

Type unions are unions of valid values rather than unions of properties. Developers often write something like this:

let animal: Dog | Cat;

…and expect animal to have the union of Dog and Cat properties. But again, that’s a mistake. This specifies animal as having a value that matches the union of valid Dog values and valid Cat values. But TypeScript will only allow you to access properties it knows are there. For now, that means properties on all the types in the union.

Narrowing

Right now, we have this:

let animal: Dog | Cat;

How do we properly treat animal as a Dog when it’s a Dog, and access properties on the Dog interface, and likewise when it’s a Cat? For now, we can use the in operator. This is an old-school JavaScript operator you probably don’t see very often, but it essentially allows us to test if a property is in an object. Like this:

let o = { a: 12 };  "a" in o; // true "x" in o; // false

It turns out TypeScript is deeply integrated with the in operator. Let’s see how:

let animal: Dog | Cat = {} as any;  if ("friendly" in animal) {   console.log(animal.friendly); } else {   console.log(animal.whiskers); }

This code produces no errors. When inside the if block, TypeScript knows there’s a friendly property, and therefore casts animal as a Dog. And when inside the else block, TypeScript similarly treats animal as a Cat. You can even see this if you hover over the animal object inside these blocks in your code editor:

Showing a tooltip open on top of a a TypeScript discriminated unions example that shows `let animal: Dog`.

Showing a tooltip open on top of a a TypeScript discriminated union example that shows `let animal: Cat`.

Discriminated unions

You might expect the blog post to end here but, unfortunately, narrowing type unions by checking for the existence of properties is incredibly limited. It worked well for our trivial Dog and Cat types, but things can easily get more complicated, and more fragile, when we have more types, as well as more overlap between those types.

This is where discriminated unions come in handy. We’ll keep everything the same from before, except add a property to each type whose only job is to distinguish (or “discriminate”) between the types:

interface Cat {   weight: number;   whiskers: number;   ANIMAL_TYPE: "CAT"; } interface Dog {   weight: number;   friendly: boolean;   ANIMAL_TYPE: "DOG"; }

Note the ANIMAL_TYPE property on both types. Don’t mistake this as a string with two different values; this is a literal type. ANIMAL_TYPE: "CAT"; means a type that holds exactly the string "CAT", and nothing else.

And now our check becomes a bit more reliable:

let animal: Dog | Cat = {} as any;  if (animal.ANIMAL_TYPE === "DOG") {   console.log(animal.friendly); } else {   console.log(animal.whiskers); }

Assuming each type participating in the union has a distinct value for the ANIMAL_TYPE property, this check becomes foolproof.

The only downside is that you now have a new property to deal with. Any time you create an instance of a Dog or a Cat, you have to supply the single correct value for the ANIMAL_TYPE. But don’t worry about forgetting because TypeScript will remind you. 🙂

Showing the TypeScript discriminated union for a createDog function that returns weight and friendly properties.
Screenshot of TypeScript displaying a warning in the code editor as a result of not providing a single value for the ANIMAL_TYPE property.

Conclusion

At the beginning of this article, I said it would make sense why weight is the only accessible property in the following example:

interface Cat {   weight: number;   whiskers: number; } interface Dog {   weight: number;   friendly: boolean; } let animal: Dog | Cat;

What we learned is that TypeScript only knows that animal could be either a Dog or a Cat, but not both. As such, all we get is weight, which is the only common property between the two.

The concept of discriminated unions is how TypeScript differentiates between those objects and does so in a way that scales extremely well, even with larger sets of objects. As such, we had to create a new ANIMAL_TYPE property on both types that holds a single literal value we can use to check against. Sure, it’s another thing to track, but it also produces more reliable results—which is what we want from TypeScript in the first place.


Demystifying TypeScript Discriminated Unions originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , ,
[Top]

5 Ways to Create a Successful Business in a Design Field

[Top]

How to Cycle Through Classes on an HTML Element

Say you have three HTML classes, and a DOM element should only have one of them at a time:

<div class="state-1"></div> <div class="state-2"></div> <div class="state-3"></div>

Now your job is to rotate them. That is, cycle through classes on an HTML element. When some event occurs, if the element has state-1 on it, remove state-1 and add state-2. If it has state-2 on it, remove that and add state-3. On the last state, remove it, and cycle back to state-1.

Example of how to Cycle Through Classes on an HTML Element. Here a large <button> with an <svg> inside cycles through state-1, state-2, and state-3 classes, turning from red to yellow to green.

It’s notable that we’re talking about 3+ classes here. The DOM has a .classList.toggle() function, even one that takes a conditional as a second parameter, but that’s primarily useful in a two-class on/off situation, not cycling through classes.

Why? There is a number of reasons. Changing a class name gives you lots of power to re-style things in the DOM, and state management like that is a cornerstone of modern web development. But to be specific, in my case, I was wanting to do FLIP animations where I’d change a layout and trigger a tween animation between the different states.

Careful about existing classes! I saw some ideas that overwrote .className, which isn’t friendly toward other classes that might be on the DOM element. All these are “safe” choices for cycling through classes in that way.

Because this is programming, there are lots of ways to get this done. Let’s cover a bunch of them — for fun. I tweeted about this issue, so many of these solutions are from people who chimed into that discussion.

A verbose if/else statement to cycle through classes

This is what I did at first to cycle through classes. That’s how my brain works. Just write out very specific instructions for exactly what you want to happen:

if (el.classList.contains("state-1")) {   el.classList.remove("state-1");   el.classList.add("state-2"); } else if (el.classList.contains("state-2")) {   el.classList.remove("state-2");   el.classList.add("state-3"); } else {   el.classList.remove("state-3");   el.classList.add("state-1"); }

I don’t mind the verbosity here, because to me it’s super clear what’s going on and will be easy to return to this code and “reason about it,” as they say. You could consider the verbosity a problem — surely there is a way to cycle through classes with less code. But a bigger issue is that it isn’t very extensible. There is no semblance of configuration (e.g. change the names of the classes easily) or simple way to add classes to the party, or remove them.

We could use constants, at least:

const STATE_1 = "state-1"; const STATE_2 = "state-2"; const STATE_3 = "state-3";  if (el.classList.contains(STATE_1)) {   el.classList.remove(STATE_1);   el.classList.add(STATE_2); } else if (el.classList.contains(STATE_2)) {   el.classList.remove(STATE_2);   el.classList.add(STATE_3); } else {   el.classList.remove(STATE_3);   el.classList.add(STATE_1); }

But that’s not wildly different or better.

RegEx off the old class, increment state, then re-add

This one comes from Tab Atkins. Since we know the format of the class, state-N, we can look for that, pluck off the number, use a little ternary to increment it (but not higher than the highest state), then add/remove the classes as a way of cycling through them:

const oldN = +/bstate-(d+)b/.exec(el.getAttribute('class'))[1]; const newN = oldN >= 3 ? 1 : oldN+1; el.classList.remove(`state-$ {oldN}`); el.classList.add(`state-$ {newN}`);

Find the index of the class, then remove/add

A bunch of techniques to cycle through classes center around setting up an array of classes up front. This acts as configuration for cycling through classes, which I think is a smart way to do it. Once you have that, you can find the relevant classes for adding and removing them. This one is from Christopher Kirk-Nielsen:

const classes = ["state-1", "state-2", "state-3"]; const activeIndex = classes.findIndex((c) => el.classList.contains(c)); const nextIndex = (activeIndex + 1) % classes.length;  el.classList.remove(classes[activeIndex]); el.classList.add(classes[nextIndex]);

Christopher had a nice idea for making the add/remove technique shorter as well. Turns out it’s the same…

el.classList.remove(classes[activeIndex]); el.classList.add(classes[nextIndex]);  // Does the same thing. el.classList.replace(classes[activeIndex], classes[nextIndex]);

Mayank had a similar idea for cycling through classes by finding the class in an array, only rather than using classList.contains(), you check the classes currently on the DOM element with what is in the array.

const states = ["state-1", "state-2", "state-3"]; const current = [...el.classList].find(cls => states.includes(cls)); const next = states[(states.indexOf(current) + 1) % states.length]; el.classList.remove(current); el.classList.add(next);

Variations of this were the most common idea. Here’s Jhey’s and here’s Mike Wagz which sets up functions for moving forward and backward.

Cascading replace statements

Speaking of that replace API, Chris Calo had a clever idea where you chain them with the or operator and rely on the fact that it returns true/false if it works or doesn’t. So you do all three and one of them will work!

 el.classList.replace("state-1", "state-2") ||  el.classList.replace("state-2", "state-3") ||  el.classList.replace("state-3", "state-1");

Nicolò Ribaudo came to the same conclusion.

Just cycle through class numbers

If you pre-configured a 1 upfront, you could cycle through classes 1-3 and add/remove them based on that. This is from Timothy Leverett who lists another similar option in the same tweet.

// Assumes a `let s = 1` upfront el.classList.remove(`state-$ {s + 1}`); s = (s + 1) % 3; el.classList.add(`state-$ {s + 1}`);

Use data-* attributes instead

Data attributes have the same specificity power, so I have no issue with this. They might actually be more clear in terms of state handling, but even better, they have a special API that makes them nice to manipulate. Munawwar Firoz had an idea that gets this down to a one-liner:

el.dataset.state = (+el.dataset.state % 3) + 1

A data attribute state machine

You can count on David Khourshid to be ready with a state machine:

const simpleMachine = {   "1": "2",   "2": "3",   "3": "1" }; el.dataset.state = simpleMachine[el.dataset.state];

You’ll almost surely want a function

Give yourself a little abstraction, right? Many of the ideas wrote code this way, but so far I’ve move it out to focus on the idea itself. Here, I’ll leave the function in. This one is from Andrea Giammarchi in which a unique function for cycling through classes is set up ahead of time, then you call it as needed:

const rotator = (classes) => ({ classList }) => {   const current = classes.findIndex((cls) => classList.contains(cls));   classList.remove(...classes);   classList.add(classes[(current + 1) % classes.length]); };  const rotate = rotator(["state-1", "state-2", "state-3"]); rotate(el);

I heard from Kyle Simpson who had this same idea, almost character for character.

Others?

There were more ideas in the replies to my original tweet, but are, best I can tell, variations on what I’ve already shared above. Apologies if I missed yours! Feel free to share your idea again in the comments here. I see nobody used a switch statements — that could be a possibility!

David Desandro went as far as recording a video, which is wonderful as it slowly abstracts the concepts further and further until it’s succinct but still readable and much more flexible:

And here’s a demo Pen with all the code for each example in there. They are numbered, so to test out another one, comment out the one that is uncommented, and uncomment another example:


How to Cycle Through Classes on an HTML Element originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , , ,
[Top]

Fancy CSS Borders Using Masks

Have you ever tried to make CSS borders in a repeating zig-zag pattern? Like where a colored section of a website ends and another differently colored section begins — not with a straight line, but angled zig zags, rounded humps, or waves. There are a number of ways you could do this sort of CSS border, dating all the way back to using a background-image. But we can get more modern and programmatic with it. In this article, we’ll look at some modern CSS mask techniques to achieve the look.

Before we dig into the technical parts, though, let’s take a look at what we are building. I have made a CSS border generator where you can easily generate any kind of border within a few seconds and get the CSS code.

Did you see that? With the CSS mask property and a few CSS gradients, we get a responsive and cool-looking border — all with CSS by itself. Not only this, but such effect can be applied to any element where we can have any kind of coloration (e.g. image, gradient, etc). We get all this without extra elements, pseudo elements, or magic numbers coming from nowhere!

Oh great! All I have to do is to copy/paste code and it’s done!

True, but it’s good to understand the logic to be able to manually adjust the code if you need to.

Masking things

Since all our effects rely on the CSS mask property, let’s take a quick refresh on how it works. Straight from the spec:

The effect of applying a mask to a graphical object is as if the graphical object will be painted onto the background through a mask, thus completely or partially masking out parts of the graphical object.

If we check the formal syntax of the mask property we can see it accepts an <image> as a value, meaning either a URL of an image or a color gradient. Gradients are what we’ll be using here. Let’s start with basic examples:

In the first example of this demo, a gradient is used to make it appear as though the image is fading away. The second example, meanwhile, also uses a gradient, but rather than a soft transition between colors, a hard color stop is used to hide (or mask) half of the image. That second example illustrates the technique we will be using to create our fancy borders.

Oh, and the CSS mask property can take multiple gradients as long as they are comma-separated. That means we have even more control to mask additional parts of the image.

That example showing multiple masking gradients may look a bit tricky at first glance, but what’s happening is the same as applying the multiple gradients on the background property. But instead of using a color that blends in with the page background, we use a “transparent” black value (#0000) for the hidden parts of the image and full black (#000) for the visible parts.

That’s it! Now we can tackle our fancy borders.

Zig-Zag CSS borders

As we saw in the video at the start of this article, the generator can apply borders on one side, two sides, or all sides. Let’s start with the bottom side using a step-by-step illustration:

  1. We start by adding the first gradient layer with a solid color (red) at the top. A height that’s equal to calc(100% - 40px) is used to leave 40px of empty space at the bottom.
  2. We add a second gradient placed at the bottom that takes the remaining height of the container. There’s a little bit of geometry happening to make this work.
Diagram showing how the shape of a zig-zag is created in CSS. An upside down triangle in blue represents the shape and green areas to the left and right of it show the leftover space that is masked out with CSS.
  1. Next, we repeat the last gradient horizontally (replacing no-repeat with repeat-x). We can already see the zig-zag shape!
  2. Gradients are known to have anti-aliasing issues creating jagged edges (especially on Chrome). To avoid this, we add a slight transition between the colors, changing blue 90deg, green 0 to green, blue 1deg 89deg, green 90deg.
  3. Then we update the colors to have a uniform shape
  4. Last, we use everything inside the mask property!

We can extract two variables from those steps to define our shape: size (40px) and angle (90deg). Here’s how we can express that using placeholders for those variables. I will be using JavaScript to replace those variables with their final values.

mask:   linear-gradient(red 0 0) top/100% calc(100% - {size}) no-repeat,   conic-gradient(     from {-angle/2} at bottom,     #0000, #000 1deg {angle - 1} ,#0000 {angle}   ) bottom/{size*2*tan(angle/2)} {size} repeat-x;

We can use CSS custom properties for the size and the angle, but trigonometric functions are unsupported features at this moment. In the future, we’ll be able to do something like this:

--size: 40px; --angle: 90deg; mask:   linear-gradient(red 0 0) top/100% calc(100% - var(--size)) no-repeat,   conic-gradient(     from calc(var(--angle)/-2) at bottom,     #0000, #000 1deg calc(var(--angle) - 1deg), #0000 var(--angle)   ) bottom/calc(var(--size)*2*tan(var(--angle)/2)) var(--size) repeat-x;

Similar to the bottom border, the top one will have almost the same code with a few adjustments:

mask:   linear-gradient(red 0 0) bottom/100% calc(100% - {size}) no-repeat,   conic-gradient(     from {180deg - angle/2} at top,     #0000, #000 1deg {angle - 1}, #0000 {angle}   ) top/{size*2*tan(angle/2)} {size} repeat-x;

We changed bottom with top and top with bottom, then updated the rotation of the gradient to 180deg - angle/2 instead of -angle/2. As simple as that!

That’s the pattern we can use for the rest of the sides, like the left:

mask:   linear-gradient(red 0 0) right/calc(100% - {size}) 100% no-repeat,   conic-gradient(     from {90deg - angle/2} at left,     #0000, #000 1deg {angle - 1}, #0000 {angle}   ) left/{size} {size*2*tan(angle/2)} repeat-y;

…and the right:

mask:   linear-gradient(red 0 0) left/calc(100% - {size}) 100% no-repeat,   conic-gradient(     from {-90deg - angle/2} at right,     #0000, #000 1deg {angle - 1}, #0000 {angle}   ) right/{size} {size*2*tan(angle/2)} repeat-y;

Let’s make the borders for when they’re applied to two sides at once. We can actually reuse the same code. To get both the top and bottom borders, we simply combine the code of both the top and bottom border.

We use the conic-gradient() of the top side, the conic-gradient() of the bottom side plus a linear-gradient() to cover the middle area.

mask:   linear-gradient(#000 0 0) center/100% calc(100% - {2*size}) no-repeat,   conic-gradient(     from {-angle/2} at bottom,     #0000, #000 1deg {angle - 1},     #0000 {angle}   ) bottom/{size*2*tan(angle/2)} {size} repeat-x;   conic-gradient(     from {180deg - angle/2} at top,      #0000, #000 1deg {angle - 1}, #0000 {angle}   ) top/{size*2*tan(angle/2)} {size} repeat-x;

The same goes when applying borders to the left and right sides together:

mask:   linear-gradient(#000 0 0) center/calc(100% - {2*size}) 100% no-repeat,   conic-gradient(     from {90deg - angle/2} at left,     #0000, #000 1deg {angle - 1}, #0000 {angle}   ) left/{size} {size*2*tan(angle/2)} repeat-y,   conic-gradient(     from {-90deg - angle/2} at right,     #0000, #000 1deg {angle - 1}, #0000 {angle}   ) right/{size} {size*2*tan(angle/2)} repeat-y;

So, if we want to apply borders to all of the sides at once, we add all the gradients together, right?

Exactly! We have four conic gradients (one on each side) and one linear-gradient() in the middle. We set a fixed angle equal to 90deg because it the only one that results in nicer corners without weird overlapping. Note that I’m also using space instead of repeat-x or repeat-y to avoid bad result on corners like this:

Resizing a container with four sides configuration

Rounded CSS borders

Now let’s tackle rounded borders!

Oh no! another long explanation with a lot of calculation?!

Not at all! There is nothing to explain here. We take everything from the zig-zag example and update the conic-gradient() with a radial-gradient(). It’s even easier because we don’t have any angles to deal with — only the size variable.

Here is an illustration for one side to see how little we need to do to switch from the zig-zag border to the rounded border:

Again, all I did there was replace the conic-gradient() with this (using placeholders for size):

background:    radial-gradient(circle farthest-side, #0000 98%, #000)    50% calc(100% + {size})/{1.85*size} {2*size} repeat-x

And this for the second one:

background:   radial-gradient(circle farthest-side, #000 98%, #0000)    bottom/{1.85*size} {2*size} repeat-x

What is the logic behind the magic numbers 1.85 and 98%?

Logically, we should use 100% instead of 98% to have a circle that touches the edges of the background area; but again, it’s the anti-aliasing issue and those jagged edges. We use a slightly smaller value to prevent weird overlapping.

The 1.85 value is more of a personal preference than anything. I initially used 2 which is the logical value to get a perfect circle, but the result doesn’t look quite as nice, so the smaller value creates a more seamless overlap between the circles.

Here’s the difference:

Now we need to replicate this for the rest of the sides, just as we did with the zig-zag CSS border.

There is a small difference, however, when applying all four sides at once. You will notice that for one of the rounded borders, I used only one radial-gradient() instead of four. That makes sense since we can repeat a circular shape over all the sides using one gradient like illustrated below:

Here’s the final CSS:

mask:   linear-gradient(#000 0 0) center/calc(100% - {1.85*size}) calc(100% - {1.85*size}) no-repeat,   radial-gradient(farthest-side,#000 98%,#0000) 0 0/{2*size} {2*size} round;

Note how I’m using the round value instead of repeat. That’s to make sure we don’t cut off any of the circles. And, again, that 1.85 value is a personal preference value.

For the other type of rounded border, we still have to use four radial gradients, but I had to introduce the CSS clip-path property to correct an overlapping issue at the corners. You can see the difference between with and without clip-path in the following demo:

It’s an eight-point path to cut the corners:

clip-path: polygon(    {2*size} 0,calc(100% - {2*size}) 0,    100% {2*size},100% calc(100% - {2*size}),    calc(100% - {2*size}) 100%,{2*size} 100%,    0 calc(100% - {2*size}),0 {2*size} );

Wavy CSS borders

Both the zig-zag and rounded CSS borders needed one gradient to get the shapes we wanted. What about a wavy sort of border? That take two gradients. Below is an illustration to understand how we create one wave with two radial gradients.

Showing three diagrams of CSS borders, each with a piece of the border and an accompanying snippet of CSS to achieve the effect.It shows how one part cuts a circular white shape out of a red rectangle. The second part showing how to create a red circle shape. The third part shows two radial gradients used to position the two circles so they combine to create the wave shape.

We repeat that shape at the bottom plus a linear gradient at the top and we get the wavy border at the bottom side.

mask:    linear-gradient(#000 0 0) top/100% calc(100% - {2*size}) no-repeat,   radial-gradient(circle {size} at 75% 100%,#0000 98%,#000) 50% calc(100% - {size})/{4*size} {size} repeat-x,   radial-gradient(circle closest-side at 25% 50%,#000 99%,#0000 101%) bottom/{4*size} {2*size} repeat-x;

We do the same process for the other sides as we did with the zig-zag and rounded CSS borders. All we need is to update a few variables to have a different wave for each side.

Showing part of the CSS for each side. You can find the full code over at the generator.

What about applying a wavy CSS border on all four sides? Will we have 9 gradients in total??”

Nope, and that’s because there is no demo where a wavy border is applied to all four sides. I was unable to find a combination of gradients that gives a good result on the corners. Maybe someone reading this knows a good approach? 😉

That’s borderline great stuff!

So, you know the ins and outs of my cool little online CSS border generator! Sure, you can use the code it spits out and do just fine — but now you have the secret sauce recipe that makes it work.

Specifically, we saw how gradients can be used to mask portions of an element. Then we went to work on multiple gradients to make certain shapes from those gradient CSS masks. And the result is a pattern that can be used along the edges of an element, creating the appearance of fancy borders that you might otherwise result to background-image for. Only this way, all it takes is swapping out some values to change the appearance rather than replace an entire raster image file or something.


Fancy CSS Borders Using Masks originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , ,
[Top]