About

Saturday, January 29, 2022

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://ift.tt/3r9ftSd) 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.



source https://css-tricks.com/from-font-value-text-decoration-thickness/

Friday, January 28, 2022

Detect Accidental Blocking Calls when Using R2DBC

A while ago, jOOQ has added the org.jetbrains:annotations dependency to the jOOQ API, in order to annotate return types with nullability information. For example, the entire DSL is non-nullable:

public interface SelectWhereStep<R extends Record>
extends SelectConnectByStep<R> {

    @NotNull @CheckReturnValue
    @Support
    SelectConditionStep<R> where(Condition condition);

    // ...
}

It makes sense to give this guarantee especially to kotlin users, as they can get rid of some of the more complex types involving things like Select!<Record!>, which now turns into at least Select<Record!>. Notice also the @CheckReturnValue annotation, which IntelliJ uses for some introspections.

Other cases are more obvious, such as:

public interface ResultQuery<R extends Record> 
extends Fields, Query, Iterable<R>, Publisher<R> {

    @Nullable
    @Blocking
    R fetchOne() throws TooManyRowsException;

    @NotNull
    @Blocking
    R fetchSingle() throws NoDataFoundException, TooManyRowsException;

    // ...
}

The difference between these two types of fetch methods is that fetchOne() expects 0-1 resulting records, whereas fetchSingle() expects exactly 1 record. Both throw exceptions otherwise, but only the latter can guarantee a non-nulllable record value.

But wait, what’s this @Blocking annotation?

In jOOQ 3.17, we’ve added the org.jetbrains.annotations.Blocking annotation to all jOOQ API that executes a query on top of JDBC. This doesn’t affect users of JDBC based jOOQ queries at all, but if you’re using R2DBC for reactive querying with jOOQ, you might be tempted to accidentally call one of jOOQ’s many many blocking execution methods.

No more! IntelliJ will now complain about such a call being inappropriate:

image

At least as soon as you enable the introspection:

image

It is up to you to decide whether you want this to be a warning or an error, but at least, you’ll notice that you’re about to do something wrong.

Eclipse does not yet support this kind of introspection. If you agree it should, you could upvote this issue here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=578310.



from Java, SQL and jOOQ. https://ift.tt/3HbzHAq
via IFTTT

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.



source https://css-tricks.com/git-switching-unstaged-changes-to-a-new-branch/

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.



source https://css-tricks.com/typescript-discriminated-unions/

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.



source https://css-tricks.com/design-systems-with-backlight/

Thursday, January 27, 2022

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.



source https://css-tricks.com/cycle-through-classes-html-element/

Wednesday, January 26, 2022

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.



source https://css-tricks.com/css-borders-using-masks/

How Do You Handle Component Spacing in a Design System?

Say you’ve got a <Card /> component. It’s highly likely it shouldn’t be butted right up against any other components with no spacing around it. That’s true for… pretty much every component. So, how do you handle component spacing in a design system?

Do you apply spacing using margin directly on the <Card />? Perhaps margin-block-end: 1rem; margin-inline-end: 1rem; so it pushes away from the two sides where more content natural flows? That’s a little presumptuous. Perhaps the cards are children inside a <Grid /> component and the grid applies a gap: 1rem. That’s awkward, as now the <Card /> component spacing is going to conflict with the <Grid /> component spacing, which is very likely not what you want, not to mention the amount of space is hard coded.

Example of a component spacing where a card component is to the left of an accordion component and above an article, with 50 pixels of spacing between all three elements. Lorem i-sum text throughout in a mono font. The card has a Calvin and Hobbes comic image.
Adding space to the inline start and block end of a card component.

Different perspectives on component spacing

Eric Bailey got into this recently and looked at some options:

  • You could bake spacing into every component and try to be as clever as you can about it. (But that’s pretty limiting.)
  • You could pass in component spacing, like <Card space="xxl" />. (That can be a good approach, likely needs more than one prop, maybe even one for each direction, which is quite verbose.)
  • You could use no component spacing and create something like a <Spacer /> or <Layout /> component specifically for spacing between components. (It breaks up the job of components nicely, but can also be verbose and add unnecessary DOM weight.)

This conversation has a wide spectrum of viewpoints, some as extreme as Max Stoiber saying just never use margin ever at all. That’s a little dogmatic for me, but I like that it’s trying to rethink things. I do like the idea of taking the job of spacing and layout away from components themselves — like, for example, those content components should completely not care where they are used and let layout happen a level up from them.

Adam Argyle predicted a few years back that the use of margin in CSS would decline as the use of gap rises. He’s probably going to end up right about this, especially now that flexbox has gap and that developers have an appetite these dats to use CSS Flexbox and Grid on nearly everything at both a macro and micro level.


How Do You Handle Component Spacing in a Design System? originally published on CSS-Tricks. You should get the newsletter and become a supporter.



source https://css-tricks.com/component-spacing-design-system/

Tuesday, January 25, 2022

How to Make a Scroll-Triggered Animation With Basic JavaScript

A little bit of animation on a site can add some flair, impress users, and get their attention. You could have them run, no matter where they are on the page, immediately when the page loads. But what if your website is fairly long so it took some time for the user to scroll down to that element? They might miss it.

You could have them run all the time, but perhaps the animation is best designed so that you for sure see the beginning of it. The trick is to start the animation when the user scrolls down to that element — scroll-triggered animation, if you will.

To tackle this we use scroll triggers. When the user scrolls down to any particular element, we can use that event to do something. It could be anything, even the beginning of an animation. It could even be scroll-triggered lazy loading on images or lazy loading a whole comments section. In that way, we won’t force users to download elements that aren’t in the viewport on initial page load. Many users may never scroll down at all, so we really save them (and us) bandwidth and load time.

Scroll triggers are very useful. There are many libraries out there that you can use to implement them, like Greensock’s popular ScrollTrigger plugin. But you don’t have to use a third-party library, particularly for fairly simple ideas. In fact, you can implement it yourself using only a small handful of vanilla JavaScript. That is what we are going to do in this article.

Here’s how we’ll make our scroll-triggered event

  • Create a function called scrollTrigger we can apply to certain elements
  • Apply an .active class on an element when it enters the viewport
  • Animate that .active class with CSS

There are times where adding a .active class is not enough. For example, we might want to execute a custom function instead. That means we should be able to pass a custom function that executes when the element is visible. Like this:

scrollTrigger('.loader', {
  cb: function(el) {
    el.innerText = 'Loading ...'
    loadContent()
  }
})

We’ll also attempt to handle scroll triggers for older non-supporting browsers.

But first, the IntersectionObserver API

The main JavaScript feature we’re going to use is the Intersection Observer. This API provides a way to asynchronously observe changes in the intersection of a target element — and it does so more in a more performant way than watching for scroll events. We will use IntersectionObserver to monitor when scrolling reaches the point where certain elements are visible on the page.

Let’s start building the scroll trigger

We want to create a function called scrollTrigger and this function should take a selector as its argument.

function scrollTrigger(selector) {
  // Multiple element can have same class/selector,
  // so we are using querySelectorAll
  let els = document.querySelectorAll(selector)
  // The above `querySelectorAll` returns a nodeList,
  // so we are converting it to an array
  els = Array.from(els)
  // Now we are iterating over the elements array
  els.forEach(el => {
    // `addObserver function` will attach the IntersectionObserver to the element
    // We will create this function next
    addObserver(el)
  })
}
// Example usage
scrollTrigger('.scroll-reveal')

Now let’s create the addObserver function that want to attach to the element using IntersectionObserver:

function scrollTrigger(selector){
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    addObserver(el)
  })
}
function addObserver(el){
    // We are creating a new IntersectionObserver instance
    let observer = new IntersectionObserver((entries, observer) => { // This takes a callback function that receives two arguments: the elements list and the observer instance.
      entries.forEach(entry => {
        // `entry.isIntersecting` will be true if the element is visible
      if(entry.isIntersecting) {
        entry.target.classList.add('active')
        // We are removing the observer from the element after adding the active class
        observer.unobserve(entry.target)
      }
    })
  })
  // Adding the observer to the element
  observer.observe(el)
}
// Example usage
scrollTrigger('.scroll-reveal')

If we do this and scroll to an element with a .scroll-reveal class, an .active class is added to that element. But notice that the active class is added as soon as any small part of the element is visible.

Animated screenshot of the scroll-triggered animation with the code for it to the left and DevTools open on the right.

But that might be overkill. Instead, we might want the .active class to be added once a bigger part of the element is visible. Well, thankfully, IntersectionObserver accepts some options for that as its second argument. Let’s apply those to our scrollTrigger function:

// Receiving options as an object
// If the user doesn't pass any options, the default will be `{}`
function scrollTrigger(selector, options = {}) {
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    // Passing the options object to the addObserver function
    addObserver(el, options)
  })
}
// Receiving options passed from the scrollTrigger function
function addObserver(el, options) {
  let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if(entry.isIntersecting) {
        entry.target.classList.add('active')
        observer.unobserve(entry.target)
      }
    })
  }, options) // Passing the options object to the observer
  observer.observe(el)
}
// Example usage 1:
// scrollTrigger('.scroll-reveal')
// Example usage 2:
scrollTrigger('.scroll-reveal', {
  rootMargin: '-200px'
})

And just like that, our first two agenda items are fulfilled!

Let’s move on to the third item — adding the ability to execute a callback function when we scroll to a targeted element. Specifically, let’s pass the callback function in our options object as cb:

function scrollTrigger(selector, options = {}) {
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    addObserver(el, options)
  })
}
function addObserver(el, options){
  let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if(entry.isIntersecting){
        if(options.cb) {
          // If we've passed a callback function, we'll call it
          options.cb(el)
        } else{
          // If we haven't, we'll just add the active class
          entry.target.classList.add('active')
        }
        observer.unobserve(entry.target)
      }
    })
  }, options)
  observer.observe(el)
}
// Example usage:
scrollTrigger('.loader', {
  rootMargin: '-200px',
  cb: function(el){
    el.innerText = 'Loading...'
    // Done loading
    setTimeout(() => {
      el.innerText = 'Task Complete!'
    }, 1000)
  }
})
An updated animated screenshot of the same scroll-triggered animation. As boxes enter the screen from the bottom, a they rotate. A "loading" message that changes to "finished loading" message is the last element to scroll into view. The code is open to the left of the animation.

Great! There’s one last thing that we need to take care of: legacy browser support. Certain browsers might lack support for IntersectionObserver, so let’s handle that case in our addObserver function:

function scrollTrigger(selector, options = {}) {
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    addObserver(el, options)
  })
}
function addObserver(el, options) {
  // Check if `IntersectionObserver` is supported
  if(!('IntersectionObserver' in window)) {
    // Simple fallback
    // The animation/callback will be called immediately so
    // the scroll animation doesn't happen on unsupported browsers
    if(options.cb){
      options.cb(el)
    } else{
      entry.target.classList.add('active')
    }
    // We don't need to execute the rest of the code
    return
  }
  let observer = new IntersectionObserver((entries, observer) =>; {
    entries.forEach(entry => {
      if(entry.isIntersecting) {
        if(options.cb) {
          options.cb(el)
        } else{
          entry.target.classList.add('active')
        }
        observer.unobserve(entry.target)
      }
    })
  }, options)
  observer.observe(el)
}
// Example usages:
scrollTrigger('.intro-text')
scrollTrigger('.scroll-reveal', {
  rootMargin: '-200px',
})
scrollTrigger('.loader', {
  rootMargin: '-200px',
  cb: function(el){
    el.innerText = 'Loading...'
    setTimeout(() => {
      el.innerText = 'Task Complete!'
    }, 1000)
  }
})

Here’s that live demo again:

And that’s all for this little journey! I hope you enjoyed it and learned something new in the process.


How to Make a Scroll-Triggered Animation With Basic JavaScript originally published on CSS-Tricks. You should get the newsletter and become a supporter.



source https://css-tricks.com/scroll-triggered-animation-vanilla-javascript/