Front End Web Development

Using “box shadows” and clip-path together

Css Tricks - Wed, 04/10/2019 - 1:55pm

Let's do a little step-by-step of a situation where you can't quite do what seems to make sense, but you can still get it done with CSS trickery. In this case, it'll be applying a shadow to a shape.

You make a box .tag { background: #FB8C00; color: #222; font: bold 32px system-ui; padding: 2rem 3rem 2rem 4rem; } You fashion it into a nice tag shape

You use clip-path because it's great for that.

.tag { /* ... */ clip-path: polygon(30px 0%, 100% 0%, 100% 100%, 30px 100%, 0 50%) } You want a shadow on it, so you...

Try to use box-shadow.

.tag { /* ... */ box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.5); }

But it doesn't work. Nothing shows up. You think you're going crazy. You assume you have the syntax wrong. You don't. The problem is that clip-path is cutting it off.

You can drop-shadow a parent element instead

There is a filter that does shadows as well: drop-shadow(). But you can't use it directly on the element because the clip-path will cut it off as well. So you make a parent:

<span class="tag-wrap"> <span class="tag"> Tag </span> </span>

You can't use box-shadow on that parent either, because the parent is still a rectangle and the shadow will look wrong. But you can use filter, and the shadow will follow the shape.

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

That's all.

The post Using “box shadows” and clip-path together appeared first on CSS-Tricks.

Under-Engineered Toggles

Css Tricks - Wed, 04/10/2019 - 5:39am

Toggles. Switches. Whatever you want to call them, they've been with us for some time and have been a dominant a staple for many form interfaces. They're even baked right into many CSS frameworks, including Bootstrap and Foundation.

It's easy to think of them in binary terms: on and off. Off and on. Click to change the state and call it a day, right? I mean, it's just a checkbox with a styled visual representation.

Well, Adrian Roselli shows us that there's a lot more to think about. And, not only that, but he shows how we can under-engineer them:

I am only going to provide styles to visually convert a standard checkbox into a visual toggle. No ARIA, no script, no special features. A progressively enhanced checkbox that will continue to work if the CSS file does not load

There's a lot to digest here. His approaches to accessibility run the gamut, from hover, active, focus and disabled states to contrast in both light and dark modes, and many things in between. What's particularly key is the progressive enhancement he mentions in that quote above.

I think the most interesting thing about Adrian’s post is just how flexible his approach is to handle any situation, including color schemes and writing modes. He also takes note of the indeterminate checkbox, that state that's nether on or off, but something perhaps in between. We have a CSS pseudo-selector for that and it could warrant a post its own, given that it's a purely visual state that cannot be set in the HTML and needs to be registered via JavaScript. It's interesting to think of an "in between" state for a switch and Adrian's use case for the default state Airplane Mode is pretty compelling.

It’s an awful lot of work that we have to do to ensure that the front-end is designed well and I think this post is the best example I’ve seen in a while as to why our work is not a problem to be solved and more of a challenge to better understand the tools of our craft.

Direct Link to ArticlePermalink

The post Under-Engineered Toggles appeared first on CSS-Tricks.

In Defense of the Ternary Statement

Css Tricks - Wed, 04/10/2019 - 4:31am

Some months ago I was on Hacker News (as one does) and I ran across a (now deleted) article about not using if statements. If you’re new to this idea (like I was), you’re in a for a real treat. Just search for "if statements" on Hacker News. You'll get articles proposing that you might not need them, articles that refer to them as a code smell and even the quintessential "considered harmful." Listen, you know a programming concept is legit when people start suggesting that using it is actually gonna hurt somebody.

And if that's not enough for you, there is always the "Anti-If Campaign." If you join, you get a nifty banner and your name on the website. IF you join. Oh the sweet, sweet irony.

The first time that I ran across this bizarre "if anathema" phenomenon, I thought it was interesting, but probably just more people mad on the internet. You are always one Google search away from finding someone who is mad about anything. Like this person who hates kittens. KITTENS.

Some time later, I was watching Linus Torvald's TED interview. In that interview, he shows two slides. The first slide contains code that he deems is "bad taste."

And the second is that same code, but in what Linus would consider, "good taste."

I realize that Linus is a bit of a polarizing figure, and you might not agree with the "good taste" vs. "bad taste" phrasing. But I think we can universally agree that the second slide is just easier on the old eye balls. It's concise, has fewer logical paths to follow, and contains no if statement. I want my code to look like that. It doesn't have to be some genius algorithm (it never will be), but I think it can be clean, and remember what Billy Corgan of Smashing Pumpkins said about cleanliness...

Cleanliness is godliness. And god is empty. Just like me.

- Billy Corgan, "Zero"

So dark! But what an amazing album.

Aside from making your code look cluttered, if statements, or "branching logic," requires your brain to hold and evaluate two separate paths at one time along with all of the things that might occur on those paths. If you nest if statements, the problem intensifies because you are creating and tracking a decision tree and your brain has to bounce around all over that tree like a drunk monkey. This kind of thing is what makes code hard to read. And remember, you should write your code thinking of the moron who comes after you that is going to have to maintain it. And that moron is probably you.

As my own favorite moron, I've been making a conscious effort lately to avoid writing if statements in my JavaScript. I don't always succeed, but what I've noticed is that at the very least, it forces me to think about solving the problem from an entirely different angle. It makes me a better developer because it compels me to engage a part of my brain that is otherwise sitting on a beanbag eating peanut M&M's while the if statement does all the work.

In the process of not writing if statements, I’ve discovered my love for the way JavaScript lets you compose conditional logic with ternary statements and logical operators. What I would like to propose to you now is that ternary has gotten a bad rap, and you can use it along with the && and || operators to write some pretty concise and readable code.

The much maligned ternary

When I first started as a programmer, people used to say, "Never use a ternary. They are too complex." So I didn’t use them. Ever. I never used a ternary. I never even bothered to question whether or not those people were right.

I don't think they were.

Ternaries are just one-line if statements. Suggesting that they are implicitly too complicated in any form is just... not true. I mean, I'm not the frostiest donut in the box, but I have no problems at all understanding a simple ternary. Is it possible that we are infantilizing ourselves here just a tad when we say to always avoid them. I think that a well-structured ternary beats an if statement every time.

Let’s take a simple example. Say we have an application where we want to test and see if the user is logged in. If they are, we send them to their profile page. Otherwise, we send them to the home page. Here is the standard if statement to do that...

if (isLogggedIn) { navigateTo('profile'); } else { navigateTo('unauthorized'); }

That's a damn simple operation to split out over six lines. SIX LINES. Remember that every time you move through a line of code, you have to remember the code that came above it and how it affects the code below it.

Now the ternary version...

isLoggedIn ? navigateTo('profile') : navigateTo('unauthorized');

Your brain only has to evaluate one line here, not six. You don’t have to move between lines, remembering what was on the line before.

One of the drawbacks to the ternary, though, is that you cannot evaluate for only one condition. Working from the previous example, if you wanted to navigate to the profile page if the user was logged in, but take no action at all if they weren't, this won't work...

// !! Doesn't Compile !! logggedIn ? navigateTo('profile')

You would have to write out an actual if statement here. Or would you?

There is a trick that you can use in JavaScript when you only want to evaluate one side of the condition and you don't want to use an if statement. You do this by leveraging the way JavaScript works with the || (or) and && (and) operators.

loggedIn && navigateTo('profile');

How does that work!?

What we're doing here is asking JavaScript, "Are both of these things true?" If the first item is false, there is no reason for the JavaScript virtual machine to execute the second. We already know that both of them aren't true because one of them is false. We're exploiting the fact that JavaScript won't bother to evaluate the second item if the first one is false. This is the equivalent of saying, "If the first condition is true, execute the second."

Now what if we wanted to flip this around? What if we wanted to navigate to the profile page only if the user is not logged in? You could just slap a ! in front of the loggedIn variable, but there is another way.

loggedIn || navigateTo('profile');

What this says is, "Are either of these things true?" If the first one is false, it has to evaluate the second to know for sure. If the first one is true though, it will never execute the second because it already knows that one of them is true; therefore the whole statement is true.

Now, is that better than just doing this?

if (!loggedIn) navigateTo('profile');

No. In that form, it is not. But here’s the thing: once you know that you can use the && and || operators to evaluate equality outside of if statements, you can use them to vastly simplify your code.

Here is a more complex example. Say we have a login function where we pass a user object. That object may be null, so we need to check local storage to see if the user has a saved session there. If they do, and they are an admin user, then we direct them to a dashboard. Otherwise, we send them to a page that tells them they are unauthorized. Here is what that looks like as a straight-up if statement.

function login(user) { if (!user) { user = getFromLocalStorage('user'); } if (user) { if (user.loggedIn && user.isAdmin) { navigateTo('dashboard'); } else { navigateTo('unauthorized'); } } else { navigateTo('unauthorized'); } }

Ouch. This is complicated because we're doing a lot of null condition checking on the user object. I don't want this post to be too strawman-y, so let's simplify this down since there is a lot of redundant code here that we would likely refactor into other functions.

function checkUser(user) { if (!user) { user = getFromLocalStorage('user'); } return user; } function checkAdmin(user) { if (user.isLoggedIn && user.isAdmin) { navigateTo('dashboard'); } else { navigateTo('unauthorized'); } } function login(user) { if (checkUser(user)) { checkAdmin(user); } else { navigateTo('unauthorized'); } }

The main login function is simpler, but that's actually more code and not necessarily “cleaner” when you consider the whole and not just the login function.

I would like to propose that we can do all of this in two lines if we forgo the if statements, embrace the ternary, and use logical operators to determine equality.

function login(user) { user = user || getFromLocalStorage('user'); user && (user.loggedIn && user.isAdmin) ? navigateTo('dashboard') : navigateTo('unauthorized') }

That's it. All of that noise generated by if statements collapses down into two lines. If the second line feels a bit long and unreadable to you, wrap it so that the conditions are on their own line.

function login(user) { user = user || getFromLocalStorage("user"); user && (user.loggedIn && user.isAdmin) ? navigateTo("dashboard") : navigateTo("unauthorized"); }

If you are worried that maybe the next person won't know about how the && and || operators work in JavaScript, add some comments, a little white space and a happy tree. Unleash your inner Bob Ross.

function login(user) { // if the user is null, check local storage to // see if there is a saved user object there user = user || getFromLocalStorage("user"); // Make sure the user is not null, and is also // both logged in and an admin. Otherwise, DENIED. &#x1f332; user && (user.loggedIn && user.isAdmin) ? navigateTo("dashboard") : navigateTo("unauthorized"); } Other things you can do

While we’re at it, here are some other tricks you can play with JavaScript conditionals.

Assignment

One of my favorite tricks (which I used above), is a one-liner to check if an item is null and then reassign it if it is. You do this with an || operator.

user = user || getFromLocalStorage('user');

And you can go on forever like this...

user = user || getFromLocalStorage('user') || await getFromDatabase('user') || new User();

This also works with the ternary...

user = user ? getFromLocalStorage('user') : new User(); Multiple conditions

You can provide multiple conditions to a ternary. For instance, if we want to log that the user has logged in and then navigate, we can do that without needing to abstract all of that into another function. Wrap it in some parentheses and provide a comma.

isLoggedIn ? (log('Logged In'), navigateTo('dashboard')) : navigateTo('unauthorized');

This also works with your && and || operators...

isLoggedIn && (log('Logged In'), navigateTo('dashboard')); Nesting ternary expressions

You can nest your ternary expressions. In his excellent article on the ternary, Eric Elliot demonstrates that with the following example...

const withTernary = ({ conditionA, conditionB }) => ( (!conditionA) ? valueC : (conditionB) ? valueA : valueB );

The most interesting thing Eric is doing there is negating the first condition so that you don’t end up with the question marks and colons together, which makes it harder to read. I would take this a step further and add a little indentation. I also added the curly braces and an explicit return because seeing one parenthesis and then immediately another makes my brain start to anticipate a function invocation that is never coming.

const withTernary = ({ conditionA, conditionB }) => { return ( (!conditionA) ? valueC : (conditionB) ? valueA : valueB ) }

As a general rule, I think that you should consider not nesting ternaries or if statements. Any of the above articles on Hacker News will shame you into the same conclusion. Although I’m not here to shame you, only to suggest that perhaps (and just maybe) you will thank yourself later if you don’t.

That’s my pitch on the misunderstood ternary and logical operators. I think that they help you write clean, readable code and avoid if statements entirely. Now if only we could get Linus Torvalds to sign off on all this as being “good taste.” I could retire early and and live the rest of my life in peace.

The post In Defense of the Ternary Statement appeared first on CSS-Tricks.

The Serif Tax

Css Tricks - Tue, 04/09/2019 - 8:19am

Fonts are vector. Vector art with more points makes for larger files than vector art with fewer points. Custom fonts are downloaded. So, fonts with less points in their vector art are smaller. That's the theory anyway. Shall we see if there is any merit to it?

Open Sans (top) and Garamond (bottom)

Let's take two fonts off of Google Fonts: Open Sans and EB Garamond. The number of points isn't a dramatic difference, but the seriffed Garamond does have more of them, particularly in looking at the serif areas.

It's not just serifs, but any complication. Consider Bleeding Cowboys, a masterpiece of a font and a favorite of pawn shops and coffee carts alike where I live in the high desert:

Let's stick to our more practical comparison.

We get some hint at the size cost by downloading the files. If we download the default "Latin" set, and compare the regular weight of both:

OpenSans-Regular.ttf 96 KB EBGaramond-Regular.ttf 545 KB

I'm not 100% sure if that's apples-to-apples there, as I'm not exactly a font file expert. Maybe EB Garamond has a whole ton of extra characters or something? I dunno. Also, we don't really use .ttf files on the web where file size actually matters, so let's toss them through Font Squirrel's generator. That should tell us whether we're actually dealing with more glyphs here.

It reports slightly different sizes than the Finder did and confirms that, yes, Garamond has way more glyphs than Open Sans.

In an attempt to compare sizes with a font file with the same number of available characters, I did a custom subset of just upper, lower, punctuation, and numbers (things that both of them will have), before outputting them as .woff2 files instead of .ttf.

After that...

opensans-regular-webfont.woff2 10 KB ebgaramond-regular-webfont.woff2 21 KB

I didn't serve them over a network with GZIP or brotli or anything, but my understanding is that WOFF2 is already compressed, so it's not super relevant.

Roughly two-to-one when comparing the file size of these two pretty popular fonts? Seems somewhat significant to me. I'm not above picking a font, assuming it works with the brand and whatnot because of size.

What made me think of this is a blog post about a font called Ping. Check out this "human hand" drawing principle it's made from:

Whoa! A single stroke? Unfortunately, I don't think actual fonts can be make from strokes, so the number-of-points savings can't come from that. I purchased the "ExtraLight" variation and the points are like this:

Still pretty lean on points.

The TTF is 244 KB, so not the sub-100 of Open Sans, but again I'm not sure how meaningful that is without a matching subset and all that. Either way, I wasn't able to do that as it's against the terms of Ping to convert it.

The post The Serif Tax appeared first on CSS-Tricks.

Using a Mixin to Take the Math out of Responsive Font Sizes

Css Tricks - Tue, 04/09/2019 - 4:20am

Responsive Font Size (RFS) is an engine that automatically calculates and updates the font-size property on elements based on the dimensions of the browser viewport.

If you’re thinking that sounds familiar, that’s because there is a slew of tools out there that offer various approaches for fluid typography. In fact, Chris compiled a bunch of those a little while back. Check that out because it’s always good to know what’s out there and what fits best for a particular task.

RFS is different in that it makes writing code for fluid type feel a lot like writing native CSS (or, more accurately, like writing with a preprocessor) directly in the stylesheets you’re already working in — only without having to wrangle and manage a bunch of media queries. It’s even compatible with Sass, Less, Stylus and PostCSS, so it plugs into just about any stack.

Just how integrated is it? Well, let’s compare a snippet for fluid typography that uses the calc() function...

html { font-size: 16px; } @media screen and (min-width: 320px) { html { font-size: calc(16px + 6 * ((100vw - 320px) / 680)); } } @media screen and (min-width: 1200px) { html { font-size: 22px; } }

...with a similar example of how it can be done with RFS in Sass:

.title { @include font-size(4rem); }

Which compiles to:

.title { font-size: 4rem; } @media (max-width: 1200px) { .title { font-size: calc(1.525rem + 3.3vw); } }

Curious how that works? Let’s check it out and then go into how to set it up for a project.

The magic behind automatic re-scaling

Here’s a graph to get a better understanding of how RFS re-scales font sizes:

Every color represents a font size that gets passed to the font-size() mixin provided by RFS. The y-axis of the graph represents the font size (in px) and the x-axis represents the width of the viewport (again, in px).

Let’s focus on the green line, which is generated by applying a mixin to an element:

.title { @include font-size(40); }

In this case, a font size of 40px is passed into the mixin. That value serves as the maximum font size of the element and reaches that size when the viewport is 1200px or wider, at which point it stays at that size.

Conversely, the font size will bottom out at 20px, never going below that mark.

Everything else? Well, that’s where the font size value is automatically calculated, using a function behind the scenes to determine the number according to the current width of the viewport.

RFS is also a little opinionated in that it limits itself to font sizes that are 20px and above. The reasoning is that smaller text (e.g. normal body text) normally does not need to flex as much and is a lot easier to manage than larger pieces of content, like titles and such. That’s very much in line with FitText, which also prefers being used on large text (even though it will not stop you from doing it).

If you’re the type of person who likes to look under the hood, the mixin for each preprocessor is available to view in the RFS GitHub repo. For example, here’s a direct link to the SCSS version. It’s a lot of math!

Note that every font size is generated in a combination of rem and vw units, but they are mapped to px in the graph to make it easier to understand. In other words, it really takes all the mathwork out of the mix.

Everything is configurable

Seriously. Every. Single. Thing.

For example, you may have wondered why the font size capped out at viewports 1200px and wider in the previous example. That can be changed, as well as a ton of other things, including:

  • Base font size: The lowest font size value.
  • Font size unit: The type of unit to use in the output value (px or em).
  • Breakpoint: The maximum width of the viewport where the font size of the element reaches its maximum value.
  • Breakpoint unit: The unit used for the media query that the mixin generates (px, em or rem).
  • Factor: This serves as a sorta volume control that informs the mixin how aggressive it should be in calculating font sizes from the maximum viewport width all the way down.
  • Rem value: This defines the value of 1rem in pixel (px) units.
  • Two dimensional: A feature that sniffs out the smallest side of a viewport and uses it to calculate the font size value. This comes in handy when, say, you’d like to keep the font from getting smaller when a device is rotated from a portrait orientation to landscape.
  • Class: Provides class names that can be added to an element in the HTML to either enable or disable fluid sizing.

So, yeah. A lot of options and flexibility here. The important thing to know is that all of these options are variables that can be defined in your stylesheets.

All this said, the default settings are pretty safe to use, and they will prevent a lot of longer words truncating from the viewport. This is especially true for some languages — like German or Dutch — that contain a lot of compound words.

Using RFS in a project

Let’s dive straight into to the code. It would be exhaustive to look at the code for each preprocessor, so I’ll be explaining everything in the .scss syntax. But if you prefer something else, you can check out the examples in other languages in the GitHub repo in the Usage section.

First and foremost, RFS needs to be installed on the project. It’s available in npm and Yarn:

## npm npm install rfs ## Yarn yarn add rfs ## Bower is available, but has been deprecated bower install rfs --save

Then, gotta make sure the mixin is imported with the rest of the styles, wherever you do your imports for other partials:

@import "~rfs/scss";

Now, we can start cooking with the mixin!

.title { color: #333; @include font-size(64px); } .subtitle { color: #666; @include font-size(48px); } .paragraph { @include font-size(16px); }

I passed values in px, but rem units are also supported. If a value without a unit is passed, px is used by default. The font sizes are always rendered in rem (in combination with vw) to make sure the font sizes also increase when the default font size is increased in the browser (this is a feature often used by visually impaired people).

The output is:

.title { color: #333; font-size: 4rem; } @media (max-width: 1200px) { .title { font-size: calc(1.525rem + 3.3vw); } } .subtitle { color: #666; font-size: 3rem; } @media (max-width: 1200px) { .subtitle { font-size: calc(1.425rem + 2.1vw); } } .paragraph { font-size: 1rem; }

Notice that the mixin is font-size(), but RFS will also let you use it in two other ways:

.title { @include font-size(4rem); // or @include responsive-font-size(64px); // or @include rfs(64); } RFS is baked right into Bootstrap

Here’s a little story for you.

One day, I had this incredibly impulsive idea to put RFS into Bootstrap. I actually did not use Bootstrap at that time, but believed it was a feature Bootstrap could definitely use. I made a pull request and waited a couple months to see what would happen.

In the meantime, I was getting more and more intrigued by Bootstrap and version 4 had just been released. Slowly but surely, I got more involved in contributing to the project and a whole new world opened for me when I discovered the community behind it. It was during hacktoberfest (oh yes, I got my t-shirt) in October 2018 that I got asked to join the Bootstrap team by mdo.

I believe contributing to open source projects is such a fun and rewarding thing. Andrés Galante has a great post on the topic if you're interested in becoming a contributor.

Since then, RFS has become a project of the Bootstrap team, and on February 11th this year, we launched Bootstrap 4.3 which includes RFS right out of the box. It’s currently disabled by default, but can easily be switched on by setting the Sass variable $enable-responsive-font-sizes: true.

But make no mistake: RFS can still be used on its own. Just cool that it's baked right into a widely used framework.

Oh yeah, let's talk browser support

Support is pretty darn good! In fact, RFS will work anywhere that supports media queries and viewport units. RFS will set a font size for Legacy browsers, like Internet Explorer 8, but the fluidity won't be there. In other words, should be safe for production!

What’s next for RFS

The next major version of Bootstrap is version 5 and we’re planning to enable RFS by default. We don’t have any plans to change the way it works for now. More than likely, the $enable-responsive-font-sizes variable will simply be set to true and that’s it.

In the future, I hope I can make use of the min() function because it would generate less CSS and make things a lot less complex. Browsers don’t seem to support this function all too well just yet, but if you’re interested in this feature, you can follow the progress in this GitHub issue.

Anything else? No, but I can leave you with a little song and dance: Na na na na, na na na na, hey hey hey goodbye!

The post Using a Mixin to Take the Math out of Responsive Font Sizes appeared first on CSS-Tricks.

Native Lazy Loading

Css Tricks - Mon, 04/08/2019 - 12:28pm

IntersectionObserver has made lazy loading a lot easier and more efficient than it used to be, but to do it really right you still gotta remove the src and such, which is cumbersome. It's definitely not as easy as:

<img src="celebration.jpg" loading="lazy" alt="..." />

Addy Osmani says it's coming in Chrome 75:

The loading attribute allows a browser to defer loading offscreen images and iframes until users scroll near them. loading supports three values:

  • lazy: is a good candidate for lazy loading.
  • eager: is not a good candidate for lazy loading. Load right away.
  • auto: browser will determine whether or not to lazily load.

I'll probably end up writing a WordPress content filter for this site that adds that attribute for every dang image on this site. Hallelujah, I say, and godspeed other browsers.

Easy lazy loading of images will have the biggest impact on the site as a whole, but lazy loaded iframes will be even bigger for the individual sites that use them. I'm into it.

Yes yes whatever native lazy loading of images but lazy loading of iframes is gonna be a goddamn game changer for ad tech: https://t.co/ADGc1UsVBf

— Laurie Voss (@seldo) April 8, 2019

I hope this pushes along the need for native aspect ratios as well, since a major reason for that is preventing content reflow from things loading later. We do have ways now, though.

The post Native Lazy Loading appeared first on CSS-Tricks.

CSS book Table of Contents &#8212; draft 1

QuirksBlog - Mon, 04/08/2019 - 6:23am

I am likely going to write a “CSS for JavaScripters” book, and therefore I need to figure out how to explain CSS to JavaScripters. This series of article snippets are a sort of try-out — pre-drafts I’d like to get feedback on in order to figure out if I’m on the right track.

Today I present the first draft of the book’s table of contents for feedback — both on the topics, and on the chapter order.

h5,h5 + ul { margin-left: 2em; } Part 1: How CSS works
  • CSS Is (Not) A Programming Language
  • Imperative vs declarative languages
  • The CSS mental model
  • A style sheet as kind-of a JSON file: all commands are executed simultaneously. (Don't take this comparison too far)
  • CSS engines in the browser
  • layout vs appearance -> layout module vs paint module. Consequences for transitions -> use opacity and transform whenever possible
  • Formal syntax: rules, selectors, declarations, values. Semi-colon is REQUIRED.
  • CSS Is Global
  • CSS classes === JS objects (good comparison? take care). Multiple classes for 'OO'
  • Backward compatibility as a design principle. Drawback: past mistakes (box model; margin collapse) won't ever be fixed.
  • Vendor prefixes. History and purpose. Current status: don't use, unless you must.
Part 2: How to work with CSS
  • Test in several browsers; start with Chrome Win/Mac/Android, Firefox, Safari Mac/iOS, Samsung Internet, and one odd one, just to keep you on your toes.
    Ex: Fonts because a beginners mistake is to assume that browsers (oses) render fonts similarly and the space is consumed unequally, depending on input (asian characters, emojis,..).
  • Browsers are a reality to be experienced.
  • A WebSite Does Not Need To Look Exactly The Same In All Browsers.
  • Trial and error is OK.
  • Put borders around the elements you're working with. Makes it easier to see what's going on. More such tricks.
  • Better to be clear but verbose than succinct but incomprehensible -> shorthand properties
    • "Shorthands reset all components to their initial value, so you can avoid inheriting things you don't intend on.
    • Things like font (which *technically* isn't a shorthand) can avoid accidentally inheriting boldness or italicness, which is useful."
    • Exceptions: margin, padding, border, border-radius
  • If the answer is "add more divs", it’s the wrong answer - or the wrong question.
  • Please give me practical tips and tricks for this chapter.
Part 3: technical chapters Section 1: What makes CSS special

Selectors should have their own chapter. Not sure about the rest, though; maybe combine them into one.

Selectors
  • how selectors are read right-to-left
    Harry: I would never encourage anyone to write selectors *for* performance, but it just so happens that the traits of a ‘good’ selector also happen to make a selector very fast.
  • https://csswizardry.com/2015/04/cyclomatic-complexity-logic-in-css/
  • and how to write them well
  • pseudos
  • focus, :hover, :focus-within (Adrian)
  • CSS selectors can be very complicated giving one great satisfaction at one’s cleverness. Avoid the temptation to be clever. In 99% of circumstances use the dumbest selectors possible.
Cascade
  • How it works; purpose; drawbacks; using !important to identify problems
  • How to work out which CSS rules apply to an element, and why they do.
  • Example: style.display = ''; allows regular cascade to return
Inheritance
  • Too little for its own chapter? Combine with cascade or specificity?
  • Example: font inheritance good
  • Example: margin inheritance bad
  • Example: opacity inheritance confusing. Font-size may have similar problems
  • cascade VS. inheritance (Harry)
Specificity
  • Formal rules
  • How one selector overrides (or doesn't) another. How specific to make your selectors.
Section 2: CSS fundamentals Units
  • px, em, rem, vw/vh PERCENTAGES, nonsense units such as cm, pica, pt, in
  • Px fundamental CSS unit; em and rem relative to font size; vw/vh relative to viewport (which viewport; what if toolbars disappear?)
  • Keywords, global such as inherit, property-specific, and nonsense keywords such as initial and unset.
  • percentages: relative to WHAT?
Box model
  • width, height, margin, padding, border. Border-box. Percentages (or in Units chapter?)
  • border vs outline
  • Mistakes were made: box model, margin collapse
  • Block-level vs inline
  • display (inline, block, grid, flex)
  • display is secretly display-inside (grid, flex) and display-outside (block, inline, table-*)
    td {display: flex} -> td is not a table cell any more; https://www.smashingmagazine.com/2019/04/display-two-value/
  • vertical aligmnent
  • Line boxes? (Here or elsewhere?) Be brief.
Layout
  • Flexbox and grid
  • More fundamental stuff? If so, what?
  • Old techniques: floats and tables. Do not use. Distrust any resource that tells you to use floats.
  • flexbox order isn't reflected in the accessibility tree
  • reordering consequences and options in css
Positioning and z-index
  • Positioning basics
  • Stacking contexts
  • * {position: relative} <- why not?
Special HTML elements
  • inputs
  • scrollbars
  • <progress>, <details>, <meter>, and <hr> are harder than they should be.
  • Table elements have their gotchas.
Advanced thingies
  • Better title needed
  • variables/custom properties: Highlight local scoping possibilities
  • calc()
  • Advanced math stuff (max() and so on) Ask Tab again in December.
  • Enough for separate chapter? Split up?
Section 3: Other topics

These are the most sketchy chapters.

Viewports
  • ? Mostly because I'm the worldwide specialist and writing this chapter would be easy. Not 100% certain it belongs in a beginners handbook, though.
  • Visual viewport, layout viewport, meta viewport.
  • Why so complicated? What problem does it solve?
Media queries and @supports
  • Especially width (and warning not to use device-width)
  • The limits of @supports (transition-property: nonsense)
CSSOM
  • Explanation.
  • Dangers of offsetWidth and related -> re-layout
  • How JavaScript can change styles and style sheets
  • Typed Opject Model https://css-tricks.com/working-with-the-new-css-typed-object-model/
Modularization
  • Not sure yet if I should include this chapter.
  • Inline CSS and why it's generally bad
  • CSS modules
  • Ordering your stylesheet http://bradfrost.com/blog/post/css-architecture-for-design-systems/
  • BEM
  • CSS in JS

Testing for Visual Regressions with Percy

Css Tricks - Mon, 04/08/2019 - 5:14am

While this post is not sponsored (it is based on Paul’s own personal experience), we have worked with Percy before on a sponsored video, that also goes through the process of setting up Percy on a site. It’s fascinating, powerful, useful stuff and I highly recommend checking it out.

Let me set the stage:

  1. You've pushed some code and all the meticulously unit tests you wrote pass. Fantastic! Let’s get this branch into QA.
  2. You receive a Slack message from your QA team asking why a button is now floating off the screen?
  3. "But... I didn't touch any code in that part of the application," you think to yourself.
  4. Then you remember you did change some CSS.
  5. Panic! What else has changed in the UI? Does this affect iPad? Will Firefox behave differently than Chrome? What about mobile?

This is a very common scenario many of us face when building large-scale applications that deal with different screen sizes and browsers. It’s a Herculean task to test UI for each and every change to code.

What now, throw in the towel and move to the mountains? Thankfully, no. We have Percy to help save the day! And it’s really the best friend we have for testing unexpected outcomes that impact design and layout. Percy has become an indispensable part of my development stack and I convinced CSS-Tricks to let me share some things about it that have made my code stronger and helped prevent errors from shipping.

Plus, it integrates well with other tooling and is a relative breeze to set up. So hang with me a bit as we walk through what Percy is and how to leverage it for your projects.

So, what exactly is Percy?

According to Percy’s site, it’s an “all in one visual review platform."

I’ve found that holds true. What it boils down to is that Percy provides a way to test visual regressions. That’s pretty awesome if you think about it. Many changes to a codebase — especially working with CSS — can introduce breaking changes to a site’s design. If you’ve ever inherited a large legacy stylesheet, modified a class, and hit Save, then you probably have a great idea of how nerve-wracking that can feel. Percy’s goal is to provide confidence in those types of situations where it’s difficult to know all of the UI that depends on the same line of code.

Excited? Let's get started.

Setting up an example site

Let’s set up a little site that Percy can hook into and test some UI we’re going to make together. These days, this couldn't be easier, thanks to Gatsby and Netlify. It is way beyond the scope of this article to do a deep dive into these technologies, but rest assured, they are wonderful as well and can get us online without a bunch of server setup.

Head over over to Netlify templates and click the "Deploy to Netlify" button, which will set up a git repo on your GitHub account and also deploy the app using Netlify.

After completing the setup steps, we should get something like this (after the site is deployed):

Magically, our site is now live! We will use this to get to grips with Percy.

Using CircleCI for automated testing

Percy works best in a continuous integration (CI) environment that will trigger testing based on an action. We will use CircleCI to make it happen by integrating with the example site’s GitHub repo and running the builds, allowing us to test every commit.

The first thing we need to do is clone down our new repo on GitHub, I clone mine as follows:

git clone https://github.com/PaulRyanStitcherAds/gatsby-starter-netlify-cms.git

With our repo now cloned, we can head over to CircleCI and sign up with a GitHub account.

We now need to add our project, so click "Add Projects" in the side navigation and you should see a screen like the following screenshot. Find the project we just cloned and click “Set Up Project."

In the Set Up Project area, we want to select Linux as our operating system and Ruby as our language (pery-cli is in Ruby). Here are the rest of the steps for this part:

CircleCI tells us that we need a .circleci directory and that we need a config.yml file in it. Create the following structure within your project.

CircleCI offers a configuration snippet to copy and paste for our configuration, but it is far too verbose for us; we only need a simple config.yml file.

Go ahead and use the following snippet. You’ll see that we install the percy-cli gem along with Percy in our tests:

version: 2 jobs: build: docker: - image: circleci/ruby:2.4.1-node-browsers working_directory: ~/repo steps: - checkout - run: name: install dependencies command: | npm install gem install percy-cli - run: name: run our tests command: | npm run build percy snapshot public

This config is all we need.

At first, It took me a while to figure out why my build was failing and turned out I was trying to install percy-cli as an npm module. Yikes!

We now have the CircleCI configuration set up so finally we can start using Percy!

As a sanity check, comment out the run our tests step above and push your code to the master branch.

Now click the "Start building" button which will use the configuration you just pushed to create a build. Here's what you should see in the workflows section:

From here on out, CircleCI will create a build for us whenever we do a push.

Hooking Percy up to CircleCI

A Percy account is needed to use the service. Head over to Percy’s site and sign up with your GitHub account.

Once signed up, you can create a new organization (if you don't already have one) and call it whatever you want.

Next thing to do is add a project to the organization. It’s probably a good idea to call the project something matching the name of the repo so that it’s recognizable later.

Now we need to add a Percy token to CircleCI. Percy tokens are located under "Project Settings."

My access token is blocked out.

Alright, let’s add the token in CircleCI in Environment Variables under “Build Settings." You can find Build Settings by clicking the gear icon beside your project in the workflows section.

Again, my token is blocked out.

It’s time to run Percy! If you commented out the run our tests line in the config file earlier, then remove the comment because that command will run Percy. Push to master again and then head over to the Percy app — we will see Percy has started its own build for creating snapshots. If all goes well, this is what we should get:

If you click on this build, then you can see all the screens Percy has snapped of the application.

You might be wondering what the deal is with that empty left column. That's where the original screen normally is, but since this is the first test, Percy informs us that there are no previous snapshots to compare.

The final thing we need to do to wrap up this connection is link our repo to Percy. So, in Percy, click “Project Settings" then click on the “install an integration" link.

Select the organization and hit install for all repositories:

Finally! We can link to our repository.

Unlocking the true power of Percy

Since we now have everything set up, we can see how Percy can be used in a code review workflow! The first thing we will do is create a branch from master. I’m calling my branch "changing-color."

Go to the /src/components/all.sass file, change Line 5 to use the color pink, then push the change to the repo. This is what we’re going to evaluate in our visual test.

Create a pull request for the change in GitHub.

CircleCI is carrying out checks for us but the one we are focused on is the Percy step. It may need a minute or two for Percy to pop up:

Percy is letting us know that we need to review changes we made, which in this case, is the change from red to pink. We can see the impact of that change:

Although the changes on the right are red, that is highlighting the areas that have been changed to pink. In other words, red is indicating the change rather than the actual appearance.

We can give this a quick glance and then click the “Approve" button which will give us a green tick on GitHub indicating we’re free to merge the pull request.

This is the true power of Percy: allowing us to see the impact of a small change and giving us the option to approve the changes.

Fantastic! We have now taking a tour on how to set Percy up in our projects along with how to integrate CircleCI. I really hope this will save you many headaches in the future when managing your UI.

The post Testing for Visual Regressions with Percy appeared first on CSS-Tricks.

Undefined: The Third Boolean Value

Css Tricks - Fri, 04/05/2019 - 4:20am

I wanted to implement a notification message in one of my projects, similar to what you’d see in Google Docs while a document is saving. In other words, a message shows up indicating that the document is saving every time a change is made. Then, once the changes are saved, the message becomes: “All changes saved in Drive.”

Let’s take a look at how we might do that using a boolean value, but actually covering three possible states. This definitely isn’t the only way to do this, and frankly, I’m not even sure if it’s the best way. Either way, it worked for me.

Here’s an example of that “Saving...” state:

The “Saving” notification displays any time a change is made to the document.

...and the “Saved” state:

The “Saved” notification displays once a change or set of changes has completed.

Using a Boolean value for to define the state was my immediate reaction. I could have a variable called isSaving and use it to render a conditional string in my template, like so:

let isSaving;

...and in the template:

<span>{{ isSaving ? ‘Saving...’ : ‘All changes saved’ }}</span>

Now, whenever we start saving, we set the value to true and then set it to false whenever no save is in progress. Simple, right?

There is a problem here, though, and it’s a bit of a UX issue. The default message is rendered as “All changes saved.” When the user initially lands on the page, there is no saving taking place and we get the “Saved” message even though no save ever happened. I would prefer showing nothing until the first change triggers the first “Saving” message.

This calls for a third state in our variable: isSaving. Now the question becomes: do we change the value to a string variable as one of the three states? We could do that, but what if we could get the third state in our current boolean variable itself?

isSaving can take two values: true or false. But what is the value directly after we have declared it in the statement: let isSaving;? It's undefined because the value of any variable is undefined when it’s declared, unless something is assigned to it. Great! We can use that initial undefined value to our advantage... but, this will require a slight change in how we write our condition in the template.

The ternary operator we are using evaluates to the second expression for anything that can’t be converted to true. The values undefined and false both are not true and, hence, resolve as false for the ternary operator. Even an if/else statement would work a similar way because else is evaluated for anything that isn’t true. But we want to differentiate between undefined and false . This is fixable by explicitly checking for false value, too, like so:

<span> {{ isSaving === true ? ‘Saving...’ : (isSaving === false ? ‘All changes saved’: ‘’) }} </span>

We are now strictly checking for true and false values. This made our ternary operator a little nested and difficult to read. If our template supports if/else statements, then we can refactor the template like this:

<span> {% if isSaving === true %} Saving... {% elseif isSaving === false %} All changes saved {% endif %} </span>

Aha! Nothing renders when the variable is neither true nor false?— exactly what we want!

The post Undefined: The Third Boolean Value appeared first on CSS-Tricks.

Revisiting the Rendering Tier

Css Tricks - Fri, 04/05/2019 - 4:17am

Have you ever created a well-intentioned, thoughtful design system only to watch it grow into an ever-increasing and scary codebase? I've been working in sort of the opposite direction, inheriting the scary codebase and trying to create a thoughtful system from it.

Here's Alex Sanders on the topic, explaining how his team at The Guardian has tackled the task of creating design systems while combating complexity:

Systems that try to contain complexity over long periods of time by convention will inevitably tend toward entropy, because one significant characteristic of convention is that it is trivially simple to break one.

You do not even need to be malicious. A convention is not a line in the sand. You can have a very good case for breaking or stretching one, but once a convention is no longer fully observed, subsequent cases for breaking or stretching it are automatically stronger, because the convention is already weakened. The more this happens, the weaker it gets.

Complexity and entropy can be two outcomes in the same situation, but need not be mutually exclusive. Interesting to think that our best intentions to guard against complexity can be somewhat destructive.

I also love how Alex explains why it’s not possible for their team to use a Tachyons-esque approach to writing styles because of the way that their development environment is kinda slow. It would be painful for the team to make that switch, despite how it could solve some other problems. This reminded me that measuring problems in this way is why there can never be a single way to write CSS. We need that inherent flexibility, even at the expense of introducing inconsistencies. Hence, conventions being less of a line in the sand and more of a guide post.

On a separate note, I really like how Alex describes styles and attributes as the reasons why his team is writing those styles. It's about aligning with business objectives:

...tens of thousands of rules that are intended to describe a maintainable set of responses to business and design problems.

That’s interesting since I don’t think we spend much time here talking specifically about the business side of CSS and the functional requirements that a styled user interface needs to accomplish.

And perhaps thinking about that can help us write better styles in the long term. Is this line of CSS solving a problem? Does this new class resolve an issue that will help our customers? These are good questions to keep in mind as we work, yet I know I don’t spend enough time thinking about them. I often see the design I’m turning into code as a problem to be solved instead.

Perhaps we should expand the way we styling a webpage because maybe, just maybe, it will help us write more maintainable code that's built to solve a real business objective.

Direct Link to ArticlePermalink

The post Revisiting the Rendering Tier appeared first on CSS-Tricks.

In search of the Miui Browser

QuirksBlog - Thu, 04/04/2019 - 5:14am

Today it’s time for an old-fashioned browser detective story about the Miui Browser, the current state of non-Google Chromia on Android, and assumptions about browsers and UA strings. In addition, this article will highlight a few principles of mobile browser research, one of which was vindicated rather nicely.

Update: severe vulnerability discovered in Miui/Mint Browser.

The old gang

A client wants me to see how the the old gang of non-Google Chromium browsers on Android is doing. We all know Samsung Internet is doing fine, thanks so much for asking. But what about the others? Several of them, such as HTC Chromium, have silently disappeared. What’s the exact current score? Who are the survivors?

When in doubt, tweet. I asked my followers for non-Google Chromium browsers, and enough of them responded to get a first impression. Samsung Internet, obviously, but also the Miui Browser for Xiaomi devices, using mostly Chromium 61, but sometimes 63. That was interesting.

The device

In order to say anything useful about such a browser, you need the device it’s made for — even though it may be available for download, as is the case for the Miui Browser. I needed a Xiaomi phone. (This, by the way, is the principle that was vindicated. We’ll get to that in due time.)

So I went to the local friendly phone shop and ordered a RedMi 5, a model that was present in several browser strings my followers sent me. (And why didn’t I order it online? Because such small shops allow you to test phones before buying them, so that I can be certain they contain the browser I want. The shop going out of business would be bad for my business. So I ordered the device there and allowed them to take their margin.)

The home screen

I did this on Thursday, and picked up the phone on Friday afternoon, politely declining their testing offer (I was tired, and the end of the week was very near). I only got around to firing it up on Tuesday. And when I did ... deception! There was just a regular boring old Google Chrome on the home screen!

Mistake #1 on my side: I plain missed the “Browser” icon in the upper left.

In my defense, this default home screen is non-standard. In fact, it’s the first time I see this particular configuration of icons.

I expected the “Browser” icon to be in the favourite bar at the bottom of the screen, in the exact spot the Chrome icon takes up. That’s the proper place for the browser that the vendor wants its users to use. If Google Chrome is there, it means the device vendor has no browser of its own. (Or that the operator put it there, but this phone was unlocked, so there’s no operator hanky-panky going on.)

I lamented the fate that made me throw away hundreds of euros on a worthless device, dutifully noted that the default browser was a Chrome 67, which after an update became a 73, yawned widely, and put the RedMi away. No Xiaomi Chromium for me or my client.

The WebView

Later that day I hit on what I thought was mistake #2. I re-checked the browser strings my followers had sent me, and sure enough, there it was, in every single browser string I received: the telltale Version/4.0 that denotes a WebView.

A WebView is a secondary browser on a mobile device that is available for apps such as Twitter and Facebook. Clicking on a link in those apps opens web pages not in the device browser, but in the device WebView. That way the users don’t have to leave the app. Engagement stuff.

On iOS you are not allowed to install other rendering and JavaScript engines. Therefore, browsers such as Chrome and Firefox iOS are not in fact Chrome or Firefox. They use the Safari WebView for their actual browsing.

On Android you can install any browser you want, but Google requires all device vendors to use the Google WebView. It is updated regularly and is at most one or two versions behind Google Chrome.

I assumed these Xiaomi users had seen my tweet in their Twitter app and clicked on the link, firing up the system WebView. It was a bit odd that all these devices used Chromium 61 instead of the expected 71 or 72 — but apparently Chinese manufacturers did not have to worry about that rule as much. Or something.

I thought I had found my mistake. In fact, I hadn’t. In fact, I’d done exactly the right thing by buying the device and ignoring the Version/4.0. But I didn’t know that yet.

Browser

On Wednesday I felt bad for my client, whom I’d promised a Xiaomi Chromium that I couldn’t deliver. So I decided to check in on the one definite non-Google Chromium: Samsung Internet. Samsung claims you can install that browser on pretty much any Android device nowadays. Let’s put that to the test and install it on the RedMi 5.

Play Store, install, no hitch, done. Load test site, works. “Make Samsung Internet your default browser?” Since I was feeling vengeful and spiteful towards Google Chrome, I decided to do it. The phone sent me to a settings menu where I could select my default browser. (Incidentally, I like this idea. Instead of allowing browsers to set themselves as the default, merely allow them to open the system setting for the default browser. This might even be a solution to the latest Google-EU conundrum.)

Anyway, the menu gave me the options for my default browser: Google Chrome, Samsung Internet ... or “Browser.”

“Browser?”

Wait a minute. There IS a vendor browser on the RedMi 5! Called Browser. (Browser and Internet are the only two names allowed for a vendor browser in Asian mythology. Don’t ask me why.)

I finally noticed the icon on the homescreen, tapped it, saw an unknown browser open before my very eyes, sent it to my test site with trembling fingers, and lo and behold, there was MiuiBrowser 10.1.2 in all its glory:

Mozilla/5.0 (Linux; U; Android 8.1.0; en-gb; Redmi Note 5 Build/OPM1.171019.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.128 Mobile Safari/537.36 XiaoMi/MiuiBrowser/10.1.2

Another surviving member of the old gang! I hadn’t wasted my client’s time and my money after all! Good days and weird browsers are here again! I was in paradise.

Then I noticed the snake: Version/4.0. A WebView.

Again the WebView

What was this? Why was this application that was clearly a browser pretending to be a WebView?

One possible answer is that it’s in fact only a skinny app that uses the WebView for all its actual browsing. I saw that before in the curious case of the Indian Micromax.

I didn’t believe this was a skinny app, though. My gut feeling told me that this is a real browser. So I downloaded an app that tests the WebView, and it gave me this for the WebView UA String on the RedMi 5:

Mozilla/5.0 (Linux; Android 8.1.0; Redmi Note 5 Build/OPM1.171019.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36

That sounds about right. It’s a Chromium 73, which fits, because I just updated the device. It has the crucial wv bit in the Android/device part of the string, which denotes a WebView.

Version/4.0 and wv

So Miui is a true browser, and not a WebView. But why does the Miui Browser incorporate Version/4.0 in its browser string?

I don’t know the answer. Maybe it wants to end up on the right side of browser detects written by clueless web developers? Maybe they just plain forgot to change it? (For a short while early in its history, Chrome’s navigator.vendor was “Apple.” They obviously forgot to change that field. A scenario like this is not impossible for Miui.)

In any case, Version/4.0 no longer necessarily denotes a WebView, though I think wv still does. So we’ll have to use that last substring to identify WebViews from now on. Duly noted.

Market share

We have now positively identified the Miui Browser as the Chromium-based system default browser on the Xiaomi RedMi 5, and likely other models as well.

The first question any web developer will ask is: OK, what’s its market share?

I don’t know. There’s only one source for this sort of data, StatCounter’s global statistics, and it doesn’t count Miui Browser as a separate browser. Miui’s stats are part of the general Google Chrome stats.

StatCounter counts Samsung Internet as a separate browser, but only after the entire Samsung Internet team threatened to holds its collective breath for days on end. Apparently nobody issued a similar threat for the Miui Browser. So we have no data.

I may be able to find data from a different source, though. Let’s see what happens. But even if that succeeds I would be hugely surprised if Miui Browser holds more than 1-2% of the mobile browser market.

Zoom reflow

If talking about market share yields nothing useful, let’s talk about features instead. Is the Miui Browser browser different from a regular Google Chrome 61?

Normal zooming: you only see part of the line.

Miui zooming: the lines reflow to fit on the screen.

Yes, it most definitely is. Miui Browser supports zoom reflow, one of my absolute favourite mobile browser features, and one that Google Chrome never supported.

If a user zooms in to a visual viewport size where lines of text do not fit on screen, most browsers do nothing. The lines go off-screen, and if users want to read them they’ll have to pan left or right. Safari, Chrome, Firefox, Samsung Internet, and most other mobile browsers work like that.

Miui Browser and Opera Mobile, however, support zoom reflow. If the user zooms in to where lines of text do not fit on screen, the browser reacts by shortening these lines, so that the text stll fits. This does wonders for readability, especially with those annoying sites that have far too long lines.

Zoom reflow has a down side as well: the browser has to recalculate the layout of most of the page. Elements may suddenly contain many more lines of text, and therefore grow in height. Other elements below the affected elements will be moved downward, even if their height isn’t adjusted. This causes massive reflows and recalculations of the layout. Worse, if the user zooms out the entire process has to run in reverse.

Despite this, the presence of zoom reflow is enough to make me consider switching devices in order to have the Miui Browser available. I simply LOVE it. (OK, Opera Mobile supports it as well.)

Variants

Christian Schaefer pointed out the Miui Browser can also be downloaded. Would that yield the same browser as the one on the device?

Christian referred me to the APK Mirror site (a site so horrible that I refuse to link to it, although it does appear to be trustworthy). At first I didn’t want to mess with a complicated site, so I just searched the Play Store for “Miui Browser” and got a hit. Download, install, try. This is the browser:

Mozilla/5.0 (Linux; U; Android 5.0.1; en-gb; GT-I9505 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.128 Mobile Safari/537.36 XiaoMi/Mint Browser/1.6.2

There are two important differences with the pure Miui on the RedMi: it’s a different device (Galaxy S4, because it happend to be within reach of my hands), and the name is not Miui Browser but Mint Browser, though it’s still branded Xiaomi. Also, the Version/4.0 is very much present in this browser, which is downloaded and thus certainly not a WebView.

But is there an actual difference in features? Yup, of course there is. Mint Browser does not support zoom reflow, instead handling zooming exactly as the other, boring browsers.

That leads to the question if the Mint Browser is truly a Miui Browser, or just a downloadable variant. I’m currently assuming the latter, and have concluded that testing on Mint Browser cannot stand in for testing on Miui Browser.

Then I gritted my teeth, steeled my soul, and dove deep into APK Mirror and its terrible process flow. It took me twenty minutes to figure out how to download something, and I could only do that because I browsed the site on Chrome Mac, then copied the correct clicks to my phone. It was an awful experience — but I heroically snatched Miui Browser 10.6 from the claws of this site:

Mozilla/5.0 (Linux; U; Android 8.0.0; en-gb; SM-G930F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.128 Mobile Safari/537.36 XiaoMi/MiuiBrowser/10.6.1

This one is now on my Galaxy S7, hence the different device string. It’s a later version than the one on the RedMi 5, though it still uses Chromium 61. But the browser name is Miui Browser, and not Mint Browser. Also, it supports zoom reflow. That’s enough for a positive identification. The APK Mirror version is a true Miui Browser.

Conclusion: non-Google Chromia

I have now identified Miui Browser as one of the few surviving non-Google Chromium browsers on Android, in addition to Samsung Internet.

Update: severe vulnerability discovered in Miui/Mint Browser.

Also, I feel vindicated by my insistence on buying an actual device. If I’d just searched on the Play Store I would have got the Mint Browser, which is not the native browser on Xiaomi devices. Now I’ve got the real thing. Also, I could experimentally verify that the APK Mirror download actually is the real thing as well.

It’s interesting that, like Samsung, Xiaomi thinks it’s useful to offer its browser for download. Also, Miui Browser is not restricted to Xiaomi phones any more, just as Samsung Internet is not restricted to Samsung phones any more. I now have a Xiaomi phone with Samsung Internet, and a Samsung phone with Miui Browser. Interoperability FTW!

It appears Samsung’s web developer communication strategy is partly being copied — and that shouldn’t come as a surprise, since it’s a pretty successful one. Web developers are aware of the existence of Samsung Internet, and can download it for testing even on non-Samsung devices. If Xiaomi made the downloading process easier, and did something about web developer outreach, it could have similar success.

That leaves two minor mysteries to be solved: its market share, and its use of Version/4.0 in the UA string of a true browser. I hope to be able to offer new insights later.

For now, though, I will continue my search for other surviving non-Google Chromia. If you think you’ve discovered one, please go here, add a note as to the device or the browser, and hit Send Info. The Internetz will thank you later.

Decaying Sites

Css Tricks - Thu, 04/04/2019 - 4:37am

Websites have a tendency to decay all by themselves. Link rot, they call it. Unpaid domain name registrations. Companies that have gone out of business. Site owners that have lost interest. What's sadder than a 404? Landing on a holding page of a URL that used to exist, but now has fallen into the hands of some domain hoarder after it expired, hoping someone will pay a premium to get it back.

That stuff is no fun. But what about sites that are totally still around, just old? What kind of fun things could we do to indicate oldness that's, like, on purpose?

On the CodePen blog, we call out blog posts that haven't been updated in at least a couple of years. We update documentation, sure, but we tend to leave blog posts alone as a historical record. So, we're pretty clear about that:

<?php if (get_the_modified_date("Y") < 2017) { ?> <p class="callout"><strong>Heads up!</strong> This blog post hasn't been updated in over 2 years. CodePen is an ever changing place, so if this post references features, you're probably better off checking the <a href="/documentation/">docs</a>. Get in touch with <a href="https://codepen.io/support/">support</a> if you have further questions.</p> <?php } ?>

We style it up like a little warning:

But what if it was less obvious? What if the text just kinda started going all to crap? Words falling off their lines and going out of focus? The older the content, the more decay:

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

What if you let a site decaye on purpose? Say, perhaps, you're holding oto client work and the client hasn't paid their bill. Dragoi Ciprian has a little idea (repo) for that. You set the due date and deadline:

var due_date = new Date('2017-02-27'); var days_deadline = 60;

Here's a demo of that. As I write, I'm 30 days into a 90-day deadline. If the demo looks blank to you, well, I guess I should have paid my bill so this code could have been removed &#x1f609;

See the Pen
not-paid Demo
by Chris Coyier (@chriscoyier)
on CodePen.

Or maybe the screen could kinda flash red, like you're getting hit in a video game.

Dave once mentioned this would be a cool browser extension, like the browser window could flash red when certain bad things are happening, like layout jank.

Or you could get all glitchy! (This demo is click-to-load, fast colors and motion warning.)

See the Pen
Glitchy loader
by Nathaniel Inman (@NathanielInman)
on CodePen.

See the Pen
CSS Glitched Text
by Lucas Bebber (@lbebber)
on CodePen.

Perhaps rather than basing things off a payment due date or the age of the content, these effects come into play based on how long it's been since the site's dependencies have been updated. Or at least had some kind of deployment push.

This is only sorta tangentially related, but it reminds me of the very, very scary game Lose/Lose:

Lose/Lose is a video-game with real life consequences. Each alien in the game is created based on a random file on the players [sic] computer. If the player kills the alien, the file it is based on is deleted. If the players [sic] ship is destroyed, the application itself is deleted.

Although touching aliens will cause the player to lose the game, and killing aliens awards points, the aliens will never actually fire at the player. This calls into question the player's mission, which is never explicitly stated, only hinted at through classic game mechanics. Is the player supposed to be an aggressor? Or merely an observer, traversing through a dangerous land?

The post Decaying Sites appeared first on CSS-Tricks.

A Couple of New Wufoo Tips

Css Tricks - Thu, 04/04/2019 - 4:33am

(This is a sponsored post.)

High fives to Wufoo, our long-time sponsor here on CSS-Tricks. It's powered the vast majority of forms I've built over the past decade. If you've never used it or heard of it: it's a form builder. It makes the arduous task of implementing forms trivially easy. Building a form on Wufoo means you'll get a form that does everything right UX-wise, gives you full design control, integrates with anything, and that you can put anywhere.

The feature list is too long to cover in the confines of a single post, so I always like to cover little bits that I've used recently and liked.

  1. They just released a much quicker way to rename a form both in the Form Manager and inside the form itself.
  2. Don't forget they have a robust API. I used the API to submit form entries on a form just the other day. I wanted to do some special things on a form, like be able to react to the DOM event of submitting the form. That's not really possible when the form is in an <iframe>, but just fine when you host the form yourself and submit via API. Worked great.

Direct Link to ArticlePermalink

The post A Couple of New Wufoo Tips appeared first on CSS-Tricks.

Fixed Headers, On-Page Links, and Overlapping Content, Oh My!

Css Tricks - Wed, 04/03/2019 - 11:38am

Let's take a basic on-page link:

<a href="#section-two">Section Two</a>

When clicked, the browser will scroll itself to the element with that ID: <section id="section-two"></section>. A browser feature as old as browsers themselves, just about.

But as soon as position: fixed; came into play, it became a bit of an issue. The browser will still jump to bring the newly targeted element into view, but that element may be obscured by a fixed position element, which is pretty bad UX.

I called this "headbutting the browswer window" nearly 10 years ago, and went over some possible solutions. Nicolas Gallager documented five different techniques. I'm even using a fixed position header here in v17 of CSS-Tricks, and I don't particularly love any of those techniques. I sort of punted on it and added top padding to all my <h3> elements, which is big enough for the header to fit there.

There is a new way though! Finally!

Šime Vidas documented this in Web Platform News. There are a bunch of CSS properties that go together as part of CSS scroll snapping, but it turns out that scroll-padding and scroll-margin can be used outside of a scroll snapping container.

body { scroll-padding-top: 70px; /* height of sticky header */ }

This only works in Chromium browsers:

See the Pen
Scroll Padding on Fixed Postion Headers
by Chris Coyier (@chriscoyier)
on CodePen.

This is such a useful thing we should hoot and holler for WebKit and Firefox to do it.

The post Fixed Headers, On-Page Links, and Overlapping Content, Oh My! appeared first on CSS-Tricks.

Responsible JavaScript

Css Tricks - Wed, 04/03/2019 - 11:38am

We just made a note about this article by Jeremy Wagner in our newsletter but it’s so good that I think it’s worth linking to again as Jeremy writes about how our obsession with JavaScript can lead to accessibility and performance issues:

What we tend to forget is that the environment websites and web apps occupy is one and the same. Both are subject to the same environmental pressures that the large gradient of networks and devices impose. Those constraints don’t suddenly vanish when we decide to call what we build “apps”, nor do our users’ phones gain magical new powers when we do so.

It’s our responsibility to evaluate who uses what we make, and accept that the conditions under which they access the internet can be different than what we’ve assumed. We need to know the purpose we’re trying to serve, and only then can we build something that admirably serves that purpose—even if it isn’t exciting to build.

That last part is especially interesting because it's in the same vein as what Chris wrote just the other day about embracing simplicity in our work. But it’s also interesting because I've overheard a lot of engineers at work asking how we might use CSS-in-JS tools like Emotion or Styled Components, both of which are totally neat in and of themselves. But my worry is about jumping to a cool tool before we understand the problem that we want to tackle first.

Jumping on a bandwagon because a Twitter celebrity told us to do so, or because Netflix uses tool X, Y or Z is not a proper response to complex problems. And this connects to what Jeremy says here:

This is not to say that inaccessible patterns occur only when frameworks are used, but rather that a sole preference for JavaScript will eventually surface gaps in our understanding of HTML and CSS. These knowledge gaps will often result in mistakes we may not even be aware of. Frameworks can be useful tools that increase our productivity, but continuing education in core web technologies is essential to creating usable experiences, no matter what tools we choose to use.

Just – yikes. This makes me very excited for the upcoming articles in the series.

Direct Link to ArticlePermalink

The post Responsible JavaScript appeared first on CSS-Tricks.

What Are Design Tokens?

Css Tricks - Wed, 04/03/2019 - 4:32am

I’ve been hearing a lot about design tokens lately, and although I’ve never had to work on a project that’s needed them, I think they’re super interesting and worth jotting down a few notes about. As I understand it, the general idea is this: design tokens are an agnostic way to store variables such as typography, color, and spacing so that your design system can be shared across platforms like iOS, Android, and regular ol’ websites.

Design tokens are starting to gain a bit of momentum in the design systems community, but they're not an entirely new concept. There’s a great talk with Jina Anne and Jon Levine from 2016 where they talk about how design tokens are used in the Lightning Design System at Salesforce. They describe the complexity of the world we’re living in where a single organization that is building multiple web apps and native applications need to feel and look the same whilst not slowing down the development team. Jina also has made an in-depth video course about design tokens and in the preview for that she writes:

With design tokens, you can capture low-level values and then use them to create the styles for your product or app. You can maintain a scalable and consistent visual system for UI development.

Let’s take just one example: you probably have a typographic scale that you want to be identical across a bunch of platforms. Instead of storing the values for that scale in a CSS file and replicating them in every app or website, they can be stored in a JSON file that will then be transformed into the code needed for all those other platforms. Something like this:

{ "global": { "type": "token", "category": "typography" }, "aliases": { "TYPE_SIZE_SM": { "value": "14px" }, "TYPE_SIZE_MD": { "value": "25px" }, "TYPE_SIZE_LG": { "value": "44px" } },

You could write your own code to take this JSON file and convert it into all the variables you might need, for example, a Sass file would depend upon these tokens and might consume them as variables to be used elsewhere in a web app. One example of a tool that can do a lot of the hard work for us is Amazon’s Style Dictionary and here’s an example of how that works in practice:

I think this is ridiculously neat stuff. And I can see how it saves a ton of duplicate code and confusion across multiple teams since it serves as a single source of truth as opposed to having several codebases that have the same design requirements and their own stylesheets to maintain. Cristiano Rastelli also wrote about managing design tokens with Style Dictionary a little while ago and goes into a lot more depth on how to get started.

Your source of truth doesn’t even have to be a JSON file! In a post from earlier this year, Pavel Laptev shows us how to make these design tokens in Figma and, by using their API, abstract those values out of design mockups and use them in a codebase.

Pavel broke up his Figma doc into separate pages for his grid, spacers, palette and typography like this:

Right now, it seems like this requires a ton of effort to set up, but I reckon that tools like Sketch and Figma are only going to make stuff like this way easier for us in the near future – they probably want the source of truth to be in their specific design tool instead of some other tool.

The last thing I want to mention is this post by Brent Jackson where he wrote up some thoughts about interoperability when it comes to design systems. Specifically, he argues that there should be a specification for design tokens so that any CSS-in-JS library could consume that code in any format or style:

Design system tokens are meant to be flexible and work cross-platform, which means different teams, different implementations, and different libraries will name things differently. This is where this specification would fit in. A lot of interoperability could be realized, if we all, for example, named our color palette colors and named the font sizes we use fontSizes. What you do beyond that and what data format you use to store these values, is up to you. It's trivial to convert JSON to ES modules to YAML or even TOML, if that's your thing. It's also just a data structure, so transforming between other data structures (e.g. design tools or a GraphQL API) should also be possible. This standard also wouldn't try to solve the extremely complex problems of how to name the colors themselves.

Brent then went ahead and created a theme specification to solve this very problem. It looks like having a single standard for writing all our variables and settings would help us if we wanted to switch from one CSS-in-JS library to another, or if we wanted to switch to some other system that we haven’t imagined yet.

Anyway, I believe that design tokens are only starting to become mainstream and their popularity is about to increase as these tools, workflows, and standards become better with time — it’s all thoroughly exciting stuff!

The post What Are Design Tokens? appeared first on CSS-Tricks.

Make it hard to screw up driven development

Css Tricks - Tue, 04/02/2019 - 11:36am

Development is complicated. Our job is an ongoing battle between getting the job done and doing that job in a safe, long-lasting way.

Developers say things like, "I'm just going to do this quick and dirty first," because it's taken as fact that if you code anything quickly, it not only will be prone to mistakes, but that you'll be deliberately not honoring established conventions and skipping tasks that make for more solid code.

There is probably no practical way to make it impossible to write sloppy, bad code, but it is fascinating to consider how tooling has evolved to make it harder.

Let's get all Poka-yoke on development.

The obvious ones are automated code quality tools.

Say you're writing JavaScript. ESLint is a mega-popular tool that looks at your code as you are writing it and lets you know about issues.

ESLint is configurable and those configurations can be enforced to a team's liking. If you'd prefer to use some strong and established conventions, I believe the most popular out there is AirBnbs configuration.

There are alternatives to everything, of course. This post isn't so much about a comprehensive tooling list as it is about considering the types of tools that help us push us toward writing better code. That said, stylelint is good for CSS, PHP_CodeSniffer is good for PHP, and Rubocop is good for Ruby.

Prettier is in a similar, but unique category. It is like a "beautifier" for your code, in that it helps you reformat it not only to look good but to follow team conventions (e.g. single quotes! Two space indentions!) as well. The most common way to use Prettier is that it runs as you save the file. So perhaps you write quickly and don't worry about formatting as much, because it happens for you the second you save. There is an interesting side-benefit of quality here as Prettier can fail, and if it does, you have a problem in the syntax of your code you need to fix. Super useful.

Prettier failing.

I'm intrigued by tools like Sonarlint, Code Climate, and Resharper that look, to me, essentially like linters, but deliver only a best-practice analysis rather than configuring things yourself. It also claims to understand your code at a deeper level. Webhint and Deepscan look similarly interesting. Feel free to correct me if I have this wrong because I haven't gotten a chance to use any of them yet.

Taking linting a step further, you can make passing lint tests a requirement before files can even be committed into Git. Git hooks are the ticket here, and the most popular tool for managing them is Husky.

Similarly, actual tests are powerful preventers of bad code.

It's always smart to write tests. Deploying code that breaks features is embarrassing, a waste of time, and can negatively impact your business. Yet we do it all too often. The whole point of tests is to prevent that.

Things like Jest for JavaScript and RSpec for Ruby are useful, and considered unit testing. It's work! You manually write functions that expect certain results. I expect that if I call a function with these parameters it returns this value!

Test-Driven Development (TDD) is a practice in which you write the test before you write the actual code that does the thing you're trying to do. It's a nice way to work if you can pull it off, as you've got code coverage from the get-go.

Another type of automated testing is integration (also known as end-to-end) testing. I'm a fan of Cypress for that. It simulates a user actually using a browser. Go to this URL! Click this! Fill out this field and submit the form! Does this thing exist now? Is the URL what it's supposed to be? Is this other thing visible? That kind of testing is powerful in that a lot of things have to be going right for these to pass, so there is a ton of implied testing.

As a CSS kinda guy, I'm also a fan of tests that watch to make sure the site looks how it's supposed to look and there aren't unintended consequences of styling changings. Percy is awesome for that (see our video).

And while we're talking about all the different types of automated testing you can do, there are all sorts of tools to automate some level of accessibiilty testing. Plus, there are tools like Calibre and SpeedCurve that automate Lighthouse for watching performance.

Languages and language features that help us, wittingly or not

Take JSX, for example. It's entirely possible to write bad HTML in JSX, but you can't write broken HTML. The component will error out entirely and you'll know as you're working. That's not even close to the reason JSX exists, but I find it an interesting side effect. I've fixed many bugs in my career that had to do with malformed HTML causing problems, ranging from tiny side effects to massive layout blunders.

Prettier is catching the problem here, but we'd see an error in the console if this compiled and went to the browser.

Similarly, a tool like Emmet can help generate valid HTML. I use Emmet all the time, and didn't even think of that until it was mentioned to me.

I also think of React features, like PropTypes, that throw errors when missing or unexpected data is thrown at them. Not to mention you can configure your linter to yell at you if you're missing the PropType. That's pretty powerful testing to be enforced for a fairly small amount of labor (compared to, say, writing a test). You can even force them to help with accessibility.

It would be impossible to not mention TypeScript here. One of the major points of using TypeScript is code safety. The fact that it's getting huge (listen to Laurie Voss on this) points to the fact that we want to enforce that safety. I remember when Angular 2 came out, there were long, solid explanations as to why. People also talk about the tooling improvements you get with TypeScript: advanced autocompletion, navigation, and refactoring. They are all, in a way, also about code safety — having the editor help you write correct file names and function names. TypeScript or not, any sort of autocomplete/IntelliSense is great to have.

The whole idea of this post came from me thinking about how GraphQL has this "you can't screw it up" quality to it. You can't ask for data that isn't there, as it will error right as you're working with it — and then you'll fix it. And you can't get back data that you aren't expecting, as you've described exactly what you want back and that's what GraphQL does. It's not that you can't write bad code that uses GraphQL or write a bad GraphQL implementation, but the technology sort of encourages better code and I'm fascinated by that.

CSS-in-JS, while that's probably too broad a term generally, applies to this discussion. Most of the solutions on that spectrum involve some kind of style scoping, and style scoping provides this "you can't screw it up" topic we're focusing on. You can't cause unintended side effects when the selector you've just written compiles to something you've never hand-written, like .SpecificComponent_root_34lkj4x.

Your co-workers are an awesome line of defense

First, give y'allselves a system. Nothing goes to the master branch directly, and everything has to be a Merge/Pull Request. That gives you a spot to talk about code quality — not to mention a place where you can run a suite of automated tests before the code is dangerously close to production.

GitLab has a concept of approvers for a Merge Request. You pick some people that have to approve the branch before it can be merged.

GitHub has the same concept with protected branches. Perhaps the best thing you can do to prevent bad code is to widen the responsibility. There is always a risk this just becomes a glance-at-the-code-for-two-seconds-and-give-it-a-&#x1f44d; motion, but that's on y'all to make sure reviews are taken seriously. I've seen lots of value in a requirement that many sets of eyeballs need to be on code before it goes out. "Given enough eyeballs, all bugs are shallow" and all that.

We'll always be screwing up code, but we can also always be finding ways not to.

The post Make it hard to screw up driven development appeared first on CSS-Tricks.

Form Validation in Under an Hour with Vuelidate

Css Tricks - Tue, 04/02/2019 - 4:25am

Form validation has a reputation for being tricky to implement. In this tutorial, we’ll break things down to alleviate some of that pain. Creating nice abstractions for forms is something that Vue.js excels at and Vuelidate is personally my favorite option for validations because it doesn't require a lot of hassle. Plus, it's really flexible, so we don’t even have to do it how I’m going to cover it here. This is just a launching point.

If you simply want to copy and paste my full working example, it’s at the end. Go ahead. I won’t tell. Then your time spent is definitely under an hour and more, like, two minutes amirite?! Ahh, the internet is a beautiful place.

You may find you need to modify the form we're using in this post so, in that case, you can read the full thing. We’ll start with a simple case and gradually build out a more concrete example. Finally, we’ll go through how to show form errors when the user has completed the form.

Simplest case: showing the entry once you’re done with the input

First, let’s show how we’d work with Vuelidate. We’ll need to create an object called validations that will mirror the data structure of what we’re trying to capture in the form. In the simplest terms, it would look like this:

data: { name: ‘’ }, validations: { name: { required } }

This would create an object within computed properties that we can find with $v. It looks like this in Vue DevTools:

A couple things to note here: $v is a computed property. This is great because that means it’s cached until something updates, which is a very performant way to deal with these state changes. Check out my article here if you want more background on this concept.

Another thing to note: there are two objects — one general object about all validations (there’s only one here currently) and one about the property name in specific. This is great because if we’re looking for general information about all fields, we have that information. And if we need to gather specific data, we have that too.

Let’s take a look at what happens when we start typing in that input:

We can see in data that we have... well, me typing like a lunatic. But let’s check out some of these other fields. $dirty, in this case, refers to whether the form has been touched at all. We can also see that the $model field is now filled in for the name object, which mirrors what’s in data.

$error and $invalid sound the same but are actually a little different. $invalid is checking if it passes validation, but $error checks both for something that's $invalid and whether or not it's $dirty (whether the form has been touched yet or not). If this all seems like a lot to parse (haha get it? parse?), don't worry, we'll walk through many of these pieces step by step.

Installing Vuelidate and creating our first form validation

OK, so that was a very simple example. Let’s build something real out of it. We’ll bring this into our application and this time we’ll make the field required and give it a minimum length requirement. In the Vue app, we’ll first add Vuelidate:

yarn add vuelidate

Now, let’s go into the main.js file and update it as follows:

import Vue from 'vue'; import Vuelidate from "vuelidate"; import App from './App.vue'; import store from './store'; Vue.use(Vuelidate); Vue.config.productionTip = false new Vue({ store, render: h => h(App) }).$mount('#app')

Now, in whatever component holds the form element, let’s first import the validators we’ll need:

import { required, minLength } from 'vuelidate/lib/validators'

Then, we’ll put the data inside of a function so we can reuse the component. You likely know about that one. Next, we’ll put our name form field in an object, because typically, we'd want to capture all of the form data together.

We’ll also need to include the validations, which will mirror our data. We’ll use required again, but this time we’ll also add a key/value pair for the minimum length of the characters, minLength(x), which will look something like this:

<script> import { required, minLength } from 'vuelidate/lib/validators' export default { data() { return { formResponses: { name: '', } } }, validations: { formResponses: { name: { required, minLength: minLength(2) }, } } } </script>

Next, in the template, we’ll create a label for accessibility purposes. Instead of using what’s in the data to create the relationship in v-model, we’ll use that computed property ($model) that we saw earlier in the validations object.

<template> <div id="app"> <label for="fname">Name*</label> <input id="fname" class="full" v-model="$v.formResponses.name.$model" type="text"> </div> </template>

Finally, beneath the form input, we’ll place some text beneath the form. We can use required attached to formResponses.name to see if it evaluates correctly and whether it’s provided at all. We can also see if there’s more than the minimum length of characters. We even have a params object that will tell us the number of characters we specified. We’ll use all of this to create informative error messages for our user.

<p class="error" v-if="!$v.formResponses.name.required">this field is required</p> <p class="error" v-if="!$v.formResponses.name.minLength">Field must have at least {{ $v.formResponses.name.$params.minLength.min }} characters.</p>

And we’ll style our error class so it’s clear at a glance that they’re errors.

.error { color: red; }

Be a little lazy

You may have noticed in that last demo that the errors are present right away and update while typing. Personally, I don’t like to show form validations that way because I think it’s distracting and confusing. What I like to do is wait to evaluate until typing has completed. For that kind of interaction, Vue comes equipped with a modifier for v-model: v-model.lazy. This will only evaluate the two-way binding once the user has completed the task with the input.

We can now improve on our single form input like this:

<label for="fname">Name*</label> <input id="fname" class="full" v-model.lazy="$v.formResponses.name.$model" type="text"> Creating custom validators

Vuelidate comes with a lot of validators out of the box, which is really helpful. However, there are times when we need something a little more custom. Let’s make a custom validator for a strong password, and check that it matches with Vuelidate’s sameAs validator

The first thing we’ll do is make a label attached to an input, and the input will be type="password".

<section> <label for="fpass1">Password*</label> <input id="fpass1" v-model="$v.formResponses.password1.$model" type="password"> </section>

In our data, we’ll create password1 and password2 (which we’ll use these in a moment to validate matching passwords) in our formResponses object, and import what we need from the validators.

import { required, minLength, email, sameAs } from "vuelidate/lib/validators"; export default { data() { return { formResponses: { name: null, email: null, password1: null, password2: null } }; },

Then, we’ll create our custom validator. In the code below you can see that we’re using regex for different types of evaluation. We’ll create a strongPassword method, passing in our password1, and then we can check it several ways with .test(), which works as you might expect: it has to pass true if it is passing and false if not.

validations: { formResponses: { name: { required, minLength: minLength(3) }, email: { required, email }, password1: { required, strongPassword(password1) { return ( /[a-z]/.test(password1) && // checks for a-z /[0-9]/.test(password1) && // checks for 0-9 /\W|_/.test(password1) && // checks for special char password1.length >= 6 ); } }, }

I am separating out each line so you can see what's going on, but we could also write the whole thing as a one-liner like this:

const regex = /^[a-zA-Z0-9!@#\$%\^\&*\)\(+=._-]{6,}$/g

I prefer to break it out because it is easier to modify.

This allows us to make the error text for our validation. We can make it say whatever we like, or even take this out of a v-if and make it present on the page. Up to you!

<section> <label for="fpass1">Password*</label> <input id="fpass1" v-model="$v.formResponses.password1.$model" type="password"> <p class="error" v-if="!$v.formResponses.password1.required">this field is required</p> <p class="error" v-if="!$v.formResponses.password1.strongPassword">Strong passwords need to have a letter, a number, a special character, and be more than 8 characters long.</p> </section>

Now we can check if the second password matches the first with Vuelidate’s sameAs method:

validations: { formResponses: { password1: { required, strongPassword(password1) { return ( /[a-z]/.test(password1) && // checks for a-z /[0-9]/.test(password1) && // checks for 0-9 /\W|_/.test(password1) && // checks for special char password1.length >= 6 ); } }, password2: { required, sameAsPassword: sameAs("password1") } } }

And we can create our second password field:

<section> <label for="fpass2">Please re-type your Password</label> <input id="fpass2" v-model="$v.formResponses.password2.$model" type="password"> <p class="error" v-if="!$v.formResponses.password2.required">this field is required</p> <p class="error" v-if="!$v.formResponses.password2.sameAsPassword">The passwords do not match.</p> </section>

Now you can see the whole thing in action all together:

Evaluate on completion

You can see how noisy that last example is until the form has been completed. In my opinion, a better route is to evaluate when the entire form is completed so the user isn't interrupted in the process. Here's how we can do that.

Remember when we looked at the computed properties $v contained? It had objects for all the individual properties, but also one for all validations as well. Inside, there were three very important values:

  • $anyDirty: if the form was touched at all or left blank
  • $invalid: if there are any errors in the form
  • $anyError: if there are any errors at all (even one), this will evaluate to true

You can use $invalid, but I prefer $anyError, because it doesn't require us to check if it’s dirty as well.

Let’s improve on our last form. We’ll put in a submit button, and a uiState string to keep track of, well, the UI state! This is incredibly useful as we can keep track of whether we’ve attempted submission, and whether we’re ready to send what we’ve collected. We’ll also make a small style improvement: position the error on the form so that it’s not moving around to in order to show the errors.

First, let’s add a few new data properties:

data() { return { uiState: "submit not clicked", errors: false, empty: true, formResponses: { ... } } }

Now, we’ll add in a submit button at the end of the form. The .prevent modifier at the end of the @click directive acts like preventDefault, and keeps the page from reloading:

<section> <button @click.prevent="submitForm" class="submit">Submit</button> </section>

We’ll handle some different states in the submitForm method. We’re going to use that computed property from Vuelidate ($anyDirty) to see if the form is empty. Remember, we can gather that information from this.$v. We used the formResponses object to hold all the form responses, so what we’ll use is this.$v.formResponses.$anyDirty. We’ll map that value to our "empty" data property. We’ll also do the same with errors and we’ll change the uiState to "submit clicked":

submitForm() { this.formTouched = !this.$v.formResponses.$anyDirty; this.errors = this.$v.formResponses.$anyError; this.uiState = "submit clicked"; if (this.errors === false && this.formTouched === false) { //this is where you send the responses this.uiState = "form submitted"; } }

If the form has no errors and it’s not empty, we’ll send the responses and change the uiState to "form submitted" as well.

Now, we can handle some states for errors and empty states as well and, finally, if the form is submitted, we’ll evaluate a success.

<section> <button @click.prevent="submitForm" class="submit">Submit</button> <p v-if="errors" class="error">The form above has errors, <br>please get your act together and resubmit </p> <p v-else-if="formTouched && uiState === 'submit clicked'" class="error">The form above is empty, <br>cmon y'all you can't submit an empty form! </p> <p v-else-if="uiState === 'form submitted'" class="success">Hooray! Your form was submitted!</p> </section>

In this form, we’ve given each section relative positioning and added a little padding at the bottom. That will allow us to give absolute positioning to the error state, which will prevent the form from moving around.

.error { color: red; font-size: 12px; position: absolute; text-transform: uppercase; }

There’s one last thing we need to do: now that we’ve placed the errors in the form absolutely, they’ll stack on top of each other unless we place them next to each other instead. We also want to check if the form is in the error state, which will be true only after the submit button is clicked. This can be a useful way of doing things- we won’t show the errors until the user is done with the form, which can be less invasive. It's up to you if you'd like to do it this way or the v-model.lazy example used in previous sections.

Our previous errors looked like this:

<section> ... <p class="error" v-if="!$v.formResponses.password2.required">this field is required</p> <p class="error" v-if="!$v.formResponses.password2.sameAsPassword">The passwords do not match.</p> </section>

Now, they’ll be contained together like this:

<p v-if="errors" class="error"> <span v-if="!$v.formResponses.password1.required">this field is required.</span> <span v-if="!$v.formResponses.password1.strongPassword">Strong passwords need to have a letter, a number, a special character, and be more than 8 characters long.</span> </p>

To make things even easier on you, there's a library that dynamically figures out what error to display based on your validation. Super cool! If you're doing something simple, it's probably too much overhead, but if you have a really complex form, it might save you time :)

And there we have it! Our form is validated and we have both errors and empty states when we need them, but none while we’re typing.

Sincere thanks to Damian Dulisz, one of the maintainers for Vuelidate, for proofing this article.

The post Form Validation in Under an Hour with Vuelidate appeared first on CSS-Tricks.

Who has the fastest website in F1?

Css Tricks - Tue, 04/02/2019 - 4:25am

Jake Archibald looks at the websites of Formula One race teams and rates their performance, carefully examining their images and digging into the waterfall of assets for each site:

Trying to use a site while on poor connectivity is massively frustrating, so anything sites can do to make it less of a problem is a huge win.

In terms of the device, if you look outside the tech bubble, a lot of users can't or don't want to pay for a high-end phone. To get a feel for how a site performs for real users, you have to look at mid-to-lower-end Android devices, which is why I picked the Moto G4.

This reminds me of Tim Kadlec’s post earlier in the year about the ethics of performance:

Poor performance can, and does, lead to exclusion. This point is extremely well documented by now, but warrants repeating. Sites that use an excess of resources, whether on the network or on the device, don’t just cause slow experiences, but can leave entire groups of people out.

Anyway, back to Jake’s post about Formula One websites. I love that Jake writes in such a way that his points aren't insulting to those who work on these sites, but hones in on what we can learn about the myriad issues that lead to bad web performance. Subsequently, Jake provides us all with a ton of useful ideas for fixing performance issues like annoying layout changes, scripts that block rendering, unused CSS issues that also block rendering, and loading states.

Oh, and this reminds me that Chris noted a while back that the loading experience for most websites can be vastly improved:

Client side rendering is so interesting. Look at this janky loading experience. The page itself isn't particularly slow, but it loads in very awkwardly. A whole thing front-end devs are going to have to get good at. pic.twitter.com/sMcD4nsL98

— Chris Coyier (@chriscoyier) October 30, 2018

Direct Link to ArticlePermalink

The post Who has the fastest website in F1? appeared first on CSS-Tricks.

KV Storage

Css Tricks - Mon, 04/01/2019 - 11:07am

localStorage is...

  • Good! It's an incredibly easy API to use.
  • localStorage.setItem('name', 'Chris'); let name = localStorage.getItem('name');
  • Bad! Philip Walton explains why:

localStorage is a synchronous API that blocks the main thread, and any time you access it you potentially prevent your page from being interactive.

Chrome has an idea (here's the proposal) for reinventing it. Ultimately the API is even simpler:

import { storage } from 'std:kv-storage'; storage.set('name', 'Chris'); storage.get('name');

But! It's async, so I can use await before I do those things without blocking anything. This demo will work in Chrome Canary right now:

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

What in all heck is up with this line?

import { storage } from 'std:kv-storage';

They are calling it a "built-in module." In other words, something you can import but it makes no network request because it's built into the browser. Pretty interesting approach.

Philip continues:

Not exposing built-in modules globally has a lot of advantages: they won't add any overhead to starting up a new JavaScript runtime context (e.g. a new tab, worker, or service worker), and they won't consume any memory or CPU unless they're actually imported. Furthermore, they don't run the risk of naming collisions with other variables defined in your code.

This is built on top of indexedDB, so if you're playing with it and need to clear the values or whatever, you do it there (DevTools > Application > Storage > IndexedDB). It'll be fascinating to see if this catches on and whether new JavaScript features are shipped as built-in modules. I have no sense of whether other browsers think this is a good idea or not.

Direct Link to ArticlePermalink

The post KV Storage appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.