Web Design Trends To Watch Out For In 2019

Multi-Line Truncation with Pure CSS

Truncating a single line of text if is fairly straightforward. Truncating multiple lines is a bit harder. Using just CSS (no JavaScript or server-side dancing) is nice for the simplicity. It’s gotten a little easier lately since Firefox (since version 68) has started supporting the ultra-bizarre -webkit-line-clamp soup method, which makes browser support for that pretty OK.

There is another way though, which is very clever and something I’d call a bonafide CSS trick. We somehow failed to cover it properly in our canonical post on line clamping, so I’ll cover it here then add it to that post. I first saw this trick on the now-dead Mobify blog, and more recently covered by Natalia Onischuk on HackingUI.

The trick uses line height for measuring

Here’s a big part of the trick. Imagine an element has a line-height of 1.4rem and you want to make sure it only shows a maximum of three lines of text. If you set the max-height to 1.4rem * 3, you’ve done it!

I’m not the worlds biggest fan of united line-height, but alas, it’s necessary here to do the math. I’m also not the biggest fan of setting it over and over on elements, so let’s set a variable we can use later, and then use it to set a global line-height.

html {   --lh: 1.4rem;   line-height: var(--lh); }

Set that max height

The truncation happens just like this:

.truncate-overflow {   --max-lines: 3;   max-height: calc(var(--lh) * var(--max-lines));   overflow: hidden; }

You actually could leave it like this. That might be good enough if you don’t care about the ellipsis.

The rest of the trick comes in when you want to display that ellipsis stuff

An ellipsis (“…”) signifies that text has been truncated and continues longer than what it displayed. Using it is probably a pretty good practice when truncating text so the content doesn’t come to an abrupt, awkward end. (Well, the content itself might be awkward anyway, but hey, you tried.)

If you set position: relative on the element, you can absolutely position the ellipsis in the bottom-right corner.

.truncate-overflow::before {   content: "...";   position: absolute;   bottom: 0;   right: 0; }

I was very tempted to, instead of setting the bottom, to set the top and use top: calc(var(--lh) * (var(--max-lines) - 1)). My thinking there was that you might as well place the ellipsis at the exact point you need it. If the text is too short, the hidden overflow will cut it off. The problem with that is that it doesn’t deal with the “exactly max lines lines” problem. The ellipsis will show when the text matches the max lines — not only when it exceeds that space.

We’ll have to get trickier!

Note that this “setting stuff in the bottom-right” thing is pretty specific to left-to-right languages. I’m going to make the demo with CSS logical properties like inset-inline-end instead of right in hopes to make it more friendly to different languages and flow scenarios.

Another limitation is that the ellipsis doesn’t attach itself to the final word since it’s absolutely placed. We’re not going to fix that as well.

Let’s cover up the ellipsis when the text is too short

This is the second part of the trick. If we position the absolute at the bottom/end of the text all the time, that’s fine. But if the text is exactly equal to the --max-lines value or less, we want to hide it.

The trick there is to make a little box that is the same background as what is behind it and set it on top of the ellipsis to cover it. We can do that with the other pseudo-element:

.truncate-overflow::after {   content: "";   position: absolute;   right: 0; /* note: not using bottom */   width: 1rem;   height: 1rem;   background: white; }

Another trick we’re using here is not setting a bottom (inset-block-end) property. This places the box at the bottom of the content rather than the bottom of the relative parent, which is very useful.

Let me make the boxes red so you can see them in these three different examples:

The top example has more than three lines, so we see the ellipsis. The second example only has two lines, so the red box shows that the ellipsis will be covered up (but imagine a white box instead). The last example shows that this system works even when the content is exactly the same as the value of --max-lines.

Demo

See the Pen
Pure CSS multi-line truncation
by Chris Coyier (@chriscoyier)
on CodePen.

I used CSS logical properties here, as the browser support has gotten pretty good. If you’re supporting any version of IE, you’ll have to switch back to using the bottom and right stuff instead.

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

Desktop

Chrome Opera Firefox IE Edge Safari
69 62* 41 No 76 12.1

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
12.2-12.3 46* No 67 75 67

The post Multi-Line Truncation with Pure CSS appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Micro Frontends

One random day not long ago, I started hearing joke after joke about “micro frontends” — sort of how I first learned about Toast. I didn’t understand the source until asking around, which uncovered this article from Cam Jackson.

In this article we’ll describe a recent trend of breaking up frontend monoliths into many smaller, more manageable pieces, and how this architecture can increase the effectiveness and efficiency of teams working on frontend code.

I’d argue it should read “front-end monoliths” and “front-end code,” but I digress already.

The idea is similar to microservices, but for the front end. So, instead of one big front-end architecture (e.g. a React app), different parts of the front end are developed entirely independent of one another, have no dependencies on each other, and can be worked on and deployed independently.

It’s one of those things where you can’t quite tell if it’s really an interesting foretelling of the future, just a niche architectural choice that happened to work for a handful of large organizations, or even just a theoretical option.

The first place my mind goes is consistency and DRY-ness. Anywhere I’ve worked, these things are a big deal and it seems like the industry at large has had endless front-end problems with shipping designs that start and stay consistent and cohesive without repeating itself with shovelfuls of technical debt. Independent front-ends sound like they might be a problem if Team B is being blocked by Team A for something not directly related, but then it introduces the problem of Team B’s output drifting towards inconsistency with Team A’s output.

The article itself talks about a browse/search landing page, a details/ordering page, and a profile page, with all three of those tackled by different independent products/teams. It sounds kinda cool and interesting to me, and it also sounds like those teams better sit right next to each other at work; otherwise this app is going the way of Frankenstein’s monster in two weeks. Styling is only lightly addressed with a, “I dunno, do a good job” sort of vibe. Teams struggle with this when they are all on the same product, so I’d have huge concerns here. The first thing I’d try to solve if this is being discussed seriously would be a design system that transcends all of it and that everyone uses without fail.

And what if those micro front ends co-exist on the same page? Use <iframe>, the article says. I can’t see a world where that leads to a good user experience. (iFrames are slow, especially many of them all booting up their own worlds — and what about elements that might overflow bounds like tooltips and menus?)

The other integration options… isolating them to their own bundles or even native web components sounds a bit better. But still, the idea of siloed development where a React component might be slapped on the same page as a Vuew component seems like a huge user penalty for a pretty specific organizational problem. Not to mention you’re losing the benefits of a shared understanding of a codebase and the benefits of a deeper technical understanding of a smaller set of tools.

I’m probably not characterizing all of this fairly, especially because the idea is rather new to me and I’ve never worked like this before.

Nader Dabit has a follow up article: Building Micro Frontends with React, Vue, and Single-spa. Just so I’m not mischaracterizing that: The idea really is that you might build a React app and I build a Vue app and we’ll slap ’em together on the same page. I definitely come from an era where we laughed-then-winced when we found sites that used multiple versions of jQuery on the same page, plus one thing that loaded all of MooTools and Prototype thrown on there seemingly by accident. We winced because that was a bucket full of JavaScript, mostly duplicated for no reason, causing bugs and slowing down the page. This doesn’t seem all that much different.

Joel Denning points out in an AMA on the subject:

I’m pointing out that we’re in the “hate without closely examining” stage of the idea. It’s entirely possible that after legitimate, close examination that the idea still fails. But too early to tell.

Fair enough.

Sorry about piling on. 😣

CSS-Tricks

,
[Top]

Improving Video Accessibility with WebVTT

“The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.”
– Tim Berners-Lee

Accessibility is an important element of web development, and with the ever-growing prevalence of video content, the necessity for captioned content is growing as well. WebVTT is a technology that solves helps with captioned content as a subtitle format that integrates easily with already-existing web APIs.

That’s what we’re going to look at here in this article. Sure, WebVTT is captioning at its most basic, but there are ways to implement it to make videos (and the captioned content itself) more accessible for users.

See the Pen
VJJMZz
by Geoff Graham (@geoffgraham)
on CodePen.

Hi, meet the WebVTT format

First and foremost: WebVTT is a type of file that contains the text “WebVTT” and lines of captions with timestamps. Here’s an example:

WEBVTT  00:00:00.000 --> 00:00:03.000 - [Birds chirping] - It's a beautiful day!  00:00:04.000 --> 00:00:07.000 - [Creek trickling] - It is indeed!  00:00:08.000 --> 00:00:10.000 - Hello there!

A little weird, but makes pretty good sense, right? As you can see, the first line is “WEBVTT” and it is followed by a time range (in this case, 0 to 3 seconds) on Line 3. The time range is required. Otherwise, the WEBVTT file will not work at all and it won’t even display or log errors to let you know. Finally, each line below a time range represents captions contained in the range.

Note that you can have multiple captions in a single time range. Hyphens may be used to indicate the start of a line, though it’s not required and more stylistic than anything else.

The time range can be one of two formats: hh:mm:ss.tt or mm:ss.tt. Each part follows certain rules:

  • Hours (hh): Minimum of two digits
  • Minutes (mm): Between 00 and 59, inclusive
  • Seconds (ss): Between 00 and 59, inclusive
  • Milliseconds (tt): Between 000 and 999, inclusive

This may seem rather daunting at first. You’re probably wondering how anyone can be expected to type and tweak this all by hand. Luckily, there are tools to make this easier. For example, YouTube can automatically caption videos for you with speech recognition in addition to allowing you to download the caption as a VTT file as well! But that’s not it. WebVTT can also be used with YouTube as well by uploading your VTT file to your YouTube video.

Once we have this file created, we can then embed it into an HTML5 video element.

<!DOCTYPE HTML> <html>   <body>     <video controls autoplay>       <source src="your_video.mp4" type="video/mp4"/>       <track default kind="captions" srclang="en" label="English" src="your_caption_file.vtt"/>     </video>   </body> </html>

The <track> tag is sort of like a script that “plays” along with the video. We can use multiple tracks in the same video element. The default attribute indicates that a the track will be enabled automatically.

Let’s run down all the <track> attributes while we’re at it:

  • srclang indicates what language the track is in.
  • kind represents the type of track it is and there are five kinds:
    • subtitles are usually translations and descriptions of different parts of a video.
    • descriptions help unsighted users understand what is happening in a video.
    • captions provide un-hearing users an alternative to audio.
    • metadata is a track that is used by scripts and cannot be seen by users.
    • chapters assist in navigating video content.
  • label is a title for the text track that appears in the caption track
  • src is the source file for the track. It cannot come from a cross-origin source unless crossorigin is specified.

While WebVTT is designed specifically for video, you can still use it with audio by placing an audio file within a <video> element.

Digging into the structure of a WebVTT file

MDN has great documentation and outlines the body structure of a WebVTT file, which consists of up to six components. Here’s how MDN breaks it down:

  • An optional byte order mark (BOM)
  • The string “WEBVTT
  • An optional text header to the right of WEBVTT.
    • There must be at least one space after WEBVTT.
    • You could use this to add a description to the file.
    • You may use anything in the text header except newlines or the string “-->“.
  • A blank line, which is equivalent to two consecutive newlines.
  • Zero or more cues or comments.
  • Zero or more blank lines.

Note: a BOM is a unicode character that indicates the unicode encoding of the text file.

Bold, italic, and underline — oh my!

We can absolutely use some inline HTML formatting in WebVTT files! These are the ones that everyone is familiar with: <b>, <i>, and <u>. You use them exactly as you would in HTML.

WEBVTT  00:00:00.000 --> 00:00:03.000 align:start This is bold text  00:00:03.000 --> 00:00:06.000 align:middle This is italic text  00:00:06.000 --> 00:00:09.000 vertical:rl align:middle This is <u>underlined  text</u>

Cue settings

Cue settings are optional strings of text used to control the position of a caption. It’s sort of like positioning elements in CSS, like being able to place captions on the video.

For example, we could place captions to the right of a cue timing, control whether a caption is displayed horizontally or vertically, and define both the alignment and vertical position of the caption.

Here are the settings that are available to us.

Setting 1: Line

line controls the positioning of the caption on the y-axis. If vertical is specified (which we’ll look at next), then line will instead indicate where the caption will be displayed on the x-axis.

When specifying the line value, integers and percentages are perfectly acceptable units. In the case of using an integer, the distance per line will be equal to the height (from a horizontal perspective) of the first line. So, for example, let’s say the height of the first line of the caption is equal to 50px, the line value specified is 2, and the caption’s direction is horizontal. That means the caption will be positioned 100px (50px times 2) down from the top, up to a maximum equal to coordinates of the boundaries of the video. If we use a negative integer, it will move upward from the bottom as the value decreases (or, in the case of vertical:lr being specified, we will move from right-to-left and vice-versa). Be careful here, as it’s possible to position the captions off-screen in addition to the positioning being inconsistent across browsers. With great power comes great responsibility!

In the case of a percentage, the value must be between 0-100%, inclusive (sorry, no 200% mega values here). Higher values will move the caption from top-to-bottom, unless vertical:lr or vertical:rl is specified, in which case the caption will move along the x-axis accordingly.

As the value increases, the caption will appear further down the video boundaries. As the value decreases (including into the negatives), the caption will appear further up.

Tough picture this without examples, right? Here’s how this translates into code:

00:00:00.000 --> 00:00:03.000 line:50% This caption should be positioned horizontally in the approximate center of the screen.
00:00:03.000 --> 00:00:06.000 vertical:lr line:50% This caption should be positioned vertically in the approximate center of the screen.
00:00:06.000 --> 00:00:09.000 vertical:rl line:-1 This caption should be positioned vertically along the left side of the video.
00:00:09.000 --> 00:00:12.000 line:0 The caption should be positioned horizontally at the top of the screen.

Setting 2: Vertical

vertical indicates the caption will be displayed vertically and move in the direction specified by the line setting. Some languages are not displayed left-to-right and instead need a top-to-bottom display.

  00:00:00.000 --> 00:00:03.000 vertical:rl This caption should be vertical.
00:00:00.000 --> 00:00:03.000 vertical:lr This caption should be vertical.

Setting 3: Position

position specifies where the caption will be displayed along the x-axis. If vertical is specified, the position will instead specify where the caption will be displayed on the y-axis. It must be an integer value between 0% and 100%, inclusive.

00:00:00.000 --> 00:00:03.000 vertical:rl position:100% This caption will be vertical and toward the bottom.  00:00:03.000 --> 00:00:06.000 vertical:rl position:0% This caption will be vertical and toward the top.

At this point, you may notice that line and position are similar to the CSS flexbox properties for align-items and justify-content, and that vertical behaves a lot like flex-direction. A trick for remembering WebVTT directions is that line specifies a position perpendicular to the flow of the text, whereas position specifies the position parallel to the flow of the text. That’s why line suddenly moves along the horizontal axis, and position moves along the vertical axis if we specify vertical.

Setting 4: Size

size specifies the width of the caption. If vertical is specified, then it will set the height of the caption instead. Like other settings, it must be an integer between 0% and 100%, inclusive.

00:00:00.000 --> 00:00:03.000 vertical:rl size:50% This caption will fill half the screen vertically.
00:00:03.000 --> 00:00:06.000 position:0% This caption will fill the entire screen horizontally.

Setting 5: Align

align specifies where the text will appear horizontally. If vertical is specified, then it will control the vertical alignment instead.

The values we’ve got are: start, middle, end, left and right. Without vertical specified, the alignments are exactly what they sound like. If vertical is specified, they effectively become top, middle (vertically), and bottom. Using start and end as opposed to left and right, respectively, is a more flexible way of allowing the alignment to be based on the unicode-bidi CSS property’s plaintext value.

Note that align is not unaffected by vertical:lr or vertical:rl.

WEBVTT  00:00:00.000 --> 00:00:03.000 align:start This caption will be on the left side of the screen.  00:00:03.000 --> 00:00:06.000 align:middle This caption will be horizontally in the middle of the screen.  00:00:06.000 --> 00:00:09.000 vertical:rl align:middle This caption will be vertically in the middle of the screen.  00:00:09.000 --> 00:00:12.000 vertical:rl align:end This caption will be vertically at the bottom right of the screen regardless of vertical:lr or vertical:rl orientation.  00:00:12.000 --> 00:00:15.000 vertical:lr align:end This caption will be vertically at the bottom of the screen, regardless of the vertical:lr or vertical:rl orientation.  00:00:12.000 --> 00:00:15.000 align:left This caption will appear on the left side of the screen.  00:00:12.000 --> 00:00:15.000 align:right This caption will appear on the right side of the screen.

WebVTT Comments

WebVTT comments are strings of text that are only visible when reading the source text of the file, the same way we think of comments in HTML, CSS, JavaScript and any other language. Comments may contain a new line, but not a blank line (which is essentially two new lines).

WEBVTT  00:00:00.000 --> 00:00:03.000 - [Birds chirping] - It's a beautiful day!  NOTE This is a comment. It will not be visible to anyone viewing the caption.  00:00:04.000 --> 00:00:07.000 - [Creek trickling] - It is indeed!  00:00:08.000 --> 00:00:10.000 - Hello there!

When the caption file is parsed and rendered, the highlighted line above will be completely hidden from users. Comments can be multi-line as well.

There are three very important characters/strings to take note of that may not be used in comments: <, &, and -->. As an alternative, you can use escaped characters instead.

Not Allowed Alternative
NOTE PB&J NOTE PB&amp;J
NOTE 5 < 7 NOTE 5 &lt; 7
NOTE puppy --> dog NOTE puppy --&gt; do

A few other interesting WebVTT features

We’re going to take a quick look at some really neat ways we can customize and control captions, but that are lacking consistent browser support, at least at the time of this writing.

Yes, we can style captions!

WebVTT captions can, in fact, be styled. For example, to style the background of a caption to be red, set the background property on the ::cue pseudo-element:

video::cue {   background: red; }

Remember how we can use some inline HTML formatting in the WebVTT file? Well, we can select those as well. For example, to select and italic (<i>) element:

video::cue(i) {   color: yellow; }

Turns out WebVTT files support a style block, a lot like the way HTML files do:

WEBVTT  STYLE ::cue {   color: blue;   font-family: "Source Sans Pro", sans-serif; }

Elements can also be accessed via their cue identifiers. Note that cue identifiers use the same escaping mechanism as HTML.

WEBVTT  STYLE ::cue(#middle cue identifier) {   text-decoration: underline; } ::cue(#cue identifier ) {   font-weight: bold;   color: red; }  first cue identifier 00:00:00.000 --> 00:00:02.000 Hello, world!  middle cue identifier 00:00:02.000 --> 00:00:04.000 This cue identifier will have an underline!  cue identifier 3 00:00:04.000 --> 00:00:06.000 This one won't be affected, just like the first one!

Different types of tags

Many tags can be used to format captions. There is a caveat. These tags cannot be used in a <track> element where kind attribute is chapters. Here are some formatting tags you can use.

The class tag

We can define classes in the WebVTT markup using a class tag that can be selected with CSS. Let’s say we have a class, .yellowish that makes text yellow. We can use the tag <c.yellowish> in a caption. We can control lots of styling this way, like the font, the font color, and background color.

/* Our CSS file */ .yellowish {   color: yellow; } .redcolor {   color: red; }
WEBVTT  00:00:00.000 --> 00:00:03.000 <c.yellowish>This text should be yellow.</c> This text will be the default color.  00:00:03.000 --> 00:00:06.000 <c.redcolor>This text should be red.</c> This text will be the default color.

The timestamp tag

If you want to make captions appear at specific times, then you will want to use timestamp tags. They’re like fine-tuning captions to exact moments in time. The tag’s time must be within the given time range of the caption, and each timestamp tag must be later than the previous.

WEBVTT  00:00:00.000 --> 00:00:07.000 This <00:00:01.000>text <00:00:02.000>will <00:00:03.000>appear <00:00:04.000>over <00:00:05.000>6 <00:00:06.000>seconds.

The voice tag

Voice tags are neat in that they help identify who is speaking.

WEBVTT  00:00:00.000 --> 00:00:03.000 <v Alice>How was your day, Bob?  00:00:03.000 --> 00:00:06.000 <v Bob>Great, yours?

The ruby tag

The ruby tag is a way to display small, annotative characters above the caption.

WEBVTT  00:00:00.000 --> 00:00:05.000 <ruby>This caption will have text above it<rt>This text will appear above the caption.

Conclusion

And that about wraps it up for WebVTT! It’s an extremely useful technology and presents an opportunity to improve your site’s accessibility a great deal, particularly if you are working with video. Try some of your own captions out yourself to get a better feel for it!

CSS-Tricks

, , ,
[Top]

So, you think you’ve got project management nailed down

(This is a sponsored post.)

Who needs a project manager? You’re an organized person who can keep track of your own work, right?

Wrong.

Well, wrong if you’re part of a team. The thing about being self-organized is that it’s related to project management but not synonymous with it. Case in point: what happens if your project relies on someone else’s involvement? Sure you’re organized, but can you always say the same about your co-workers? Chances are you need something to keep everyone in sync so that a project stays on course.

That’s where you should consider trying monday.com.

monday.com is project management, but with a human touch. Sure, there’s task lists, assignments, milestones, due dates, and such like you would expect from any project management tool. That’s a given. That said, monday.com takes things up a notch by stripping away the barriers that prevent team members from collaborating with one another. For example, monday.com includes real-time messaging, file sharing, reporting, and a slew of other features that bridge the gaps between people and tasks so that everyone has purview into the progress of a project. Plus, it’s so pretty to look at.

There’s so much more than meets the eye because monday.com goes beyond project management. There’s resource management that ensures you have the right tools for a project, forecasting to affirm the prospect of a business opportunity, and even client management services. Seriously, your team and perhaps company can lean into monday.com and get a ton of use out of it.

You know what to do from here. Give monday.com a try. There’s a free trial and we’re sure you’ll find it to be so useful that you’ll want to stick with it well beyond.

Get Started

Direct Link to ArticlePermalink

The post So, you think you’ve got project management nailed down appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Finally… A Post on Finally in Promises

“When does finally fire in a JavaScript promise?” This is a question I was asked in a recent workshop and I thought I’d write up a little post to clear up any confusion.

The answer is, to quote Snape:

snape saying always

…always.

The basic structure is like this:

try {   // I’ll try to execute some code for you } catch(error) {   // I’ll handle any errors in that process }  finally {   // I’ll fire either way }

Take, for instance, this example of a Chuck Norris joke generator, complete with content populated from the Chuck Norris Database API. (Aside: I found this API from Todd Motto’s very awesome list of open APIs, which is great for demos and side projects.)

See the Pen
finally! chuck norris jokes!
by Sarah Drasner (@sdras)
on CodePen.

async function getData() {   try {     let chuckJokes = await fetch(`https://api.chucknorris.io/jokes/random`)       .then(res => res.json())          console.log('I got some data for you!')     document.getElementById("quote").innerHTML = chuckJokes.value;   }    catch(error) {     console.warn(`We have an error here: $ {error}`)      finally {     console.log('Finally will fire no matter what!')    }

In the console:

Console that says: I got some data for you! and Finally will fire no matter what!

Now, let’s introduce a typo to the API and accidentally put a bunch of r‘s in the URL of the API. This will result in our try statement failing, which means the catch now throws an error.

async function getData() {   try {     // let's mess this up a bit     let chuckJokes = await fetch(`https://api.chucknorrrrris.io/jokes/random`)       .then(res => res.json())          console.log('I got some data for you!')     document.getElementById("quote").innerHTML = chuckJokes.value;   }    catch(error) {     console.warn(`We have an error here: $ {error}`)   }   finally {     console.log('Finally will fire no matter what!')   } }

Console:

Console that has a failed GET request and then a warning that says We have an error here, Typeerror: failed to fetch, and then on a new line, Finally will fire no matter what!

One giant important piece that the example doesn’t illustrate is that the finally block will run even if in the try or catch block, a return or break statement stops the code.

When would you use this?

I’ve found it particularly useful in two different situations, though I’m sure there are others:

  • When I otherwise would duplicate code that’s need in the try and catch blocks. Here’s an example in a Vue cookbook recipe I wrote. I shut off the loading state in a finally block. This includes, like the example above, where I need to change the UI somehow in either case.
  • When there’s some cleanup to do. Oracle mentions this in their documentation. It’s Java, but the same premises apply.

Finally is not useful as often as try and catch, but worth noting for some use cases. Hope that clears it up!

The post Finally… A Post on Finally in Promises appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

A Peek at New Methods Coming to Promises

Promises are one of the most celebrated features introduced to JavaScript. Having a native asynchronous artifact baked right into the language has opened up a new era, changing not only how we write code but also setting up the base for other freat APIs — like fetch!

Let’s step back a moment to recap the features we gained when they were initially released and what new bells and whistles we’re getting next.

New to the concept of Promises? I highly recommend Jake Archibald’s article as a primer.

Features we have today

Let’s take a quick look at some of the things we can currently do with promises. When JavaScript introduced them, it gave us an API to execute asynchronous actions and react to their succesful return or failure, a way to create an association around some data or result which value we still don’t know.

Here are the Promises features we have today.

Handling promises

Every time an async method returns a promise — like when we use fetch — we can pipe then() to execute actions when the promise is fulfilled, and catch() to respond to a promise being rejected.

The classic use case is calling data from an API and either loading the data when it returns or displaying an error message if the data couldn’t be located.

fetch('//resource.to/some/data')   .then(result => console.log('we got it', result.json()))   .catch(error => console.error('something went wrong', error))

In addition, in its initial release we got two methods to handle groups of Promises.

Resolving and rejecting collections of promises

A promise can be fulfilled when it was successfully resolved, rejected when it was resolved with an error, and pending while there’s no resolution. A promise is considered settled when it has been resolved, disregarding the result.

As such, there are two methods we have to help with the behavior of handling a group of promises depending on the combination of states we obtain.

Promise.all is one or those methods. It fulfills only if all promises were resolved successfully, returning an array with the result for each one. If one of the promises fails, Promise.all will go to catch returning the reason of the error.

Promise.all(     fetch('//resource.to/some/data'),     fetch('//resource.to/more/data')   ])   .then(results => console.log('We got an array of results', results)   .catch(error => console.error('One of the promises failed', error)

In this case, Promise.all will short-circuit and go to catch as soon as one of the members of the collections throws an error, or settle when all promises are fulfilled.

Check out this short writing about promises states by Domenic Denicola for a more detailed explanation about the wording and concepts about them.

We also have Promise.race, which immediately resolves to the first promise it gets back, whether it was fulfilled or rejected. After the first promise gets resolved, the remaining ones are ignored.

Promise.race([     fetch('//resource.to/some/data'),     fetch('//resource.to/other/data')   ])   .then(result => console.log('The first promise was resolved', result))   .catch(reason => console.error('One of the promises failed because', reason))

The new kids on the block

OK, we’re going to turn our attention to new promise features we can look forward to.

Promise.allSettled

The next proposed introduction to the family is Promise.allSettled which, as the name indicates, only moves on when the entire collection members in the array are no longer in a pending status, whether they were rejected or fulfilled.

Promise.allSettled([     fetch('//resource.to/some/data'),     fetch('//resource.to/more/data'),     fetch('//resource.to/even/more/data')   ])   .then(results => {     const fulfilled = results.filter(r => r.status === 'fulfilled')     const rejected = results.filter(r => r.status === 'rejected')   })

Notice how this is different from Promise.all in that we will never enter in the catch statement. This is really good if we are waiting for sets of data that will go to different parts of a web application but want to provide more specific messages or execute different actions for each outcome.

Promise.any

The next new method is Promise.any, which lets us react to any fulfilled promise in a collection, but only short-circuit when all of them failed.

Promise.any([     fetch('//resource.to/some/data'),     fetch('//resource.to/more/data'),     fetch('//resource.to/even/more/data')   ])   .then(result => console.log('a batch of data has arrived', result))   .catch(() => console.error('all promises failed'))

This is sort of like Promise.race except that Promise.race short-circuits on the first resolution. So, if the first promise in the set resolves with an error, Promise.race moves ahead. Promise.any will continue waiting for the rest of the items in the array to resolve before moving forward.

Demo

Some of these are much easier to understand with a visual, so I put together a little playground that shows the differences between the new and existing methods.

Wrap-up

Though they are still in proposal stage, there are community scripts that emulate the new methods we covered in this post. Things like Bluebird’s any and reflect are good polyfills while we wait for browser support to improve. They also show how the community is already using these kind of asynchronous patterns, but having them built-in will open the possibilities for new patterns in data fetching and asynchronous resolution for web applications.

If you want to know more about the, the V8 blog just published a short explanation with links to the official spec and proposals.

The post A Peek at New Methods Coming to Promises appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Design Deals for the Week

[Top]

Managing Multiple Backgrounds with Custom Properties

One cool thing about CSS custom properties is that they can be a part of a value. Let’s say you’re using multiple backgrounds to pull off a a design. Each background will have its own color, image, repeat, position, etc. It can be verbose!

You have four images:

body {      background-position:     top 10px left 10px,     top 10px right 10px,     bottom 10px right 10px,     bottom 10px left 10px;      background-repeat: no-repeat;      background-image:     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);    }

You want to add a fifth in a media query:

@media (min-width: 1500px) {   body {     /* REPEAT all existing backgrounds, then add a fifth. */   } }

That’s going to be super verbose! You’ll have to repeat each of those four images again, then add the fifth. Lots of duplication there.

One possibility is to create a variable for the base set, then add the fifth much more cleanly:

body {   --baseBackgrounds:      url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);    background-position:     top 10px left 10px,     top 10px right 10px,     bottom 10px right 10px,     bottom 10px left 10px;      background-repeat: no-repeat;      background-image: var(--baseBackgrounds); } @media (min-width: 1500px) {   body {     background-image:        var(--baseBackgrounds),       url(added-fifth-background.svg);   } }

But, it’s really up to you. It might make more sense and be easier manage if you made each background image into a variable, and then pieced them together as needed.

body {   --bg1: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg);   --bg2: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg);   --bg3: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg);   --bg4: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);   --bg5: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);      background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4); } @media (min-width: 1500px) {   body {     background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4), var(--bg5);   } }

Here’s a basic version of that, including a supports query:

See the Pen
Multiple BGs with Custom Properties
by Chris Coyier (@chriscoyier)
on CodePen.

Dynamically changing just the part of a value is a huge strength of CSS custom properties!

Note, too, that with backgrounds, it might be best to include the entire shorthand as the variable. That way, it’s much easier to piece everything together at once, rather than needing something like…

--bg_1_url: url(); --bg_1_size: 100px; --bg_1_repeat: no-repeat; /* etc. */

It’s easier to put all of the properties into shorthand and use as needed:

body {     --bg_1: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg) top 10px left 10px / 86px no-repeat;   --bg_2: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg) top 10px right 10px / 86px no-repeat;   --bg_3: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg) bottom 10px right 10px / 86px no-repeat;   --bg_4: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg) bottom 10px left 10px  / 86px no-repeat;        background:     var(--bg_1), var(--bg_2),var(--bg_3),var(--bg_4); }

Like this.

The post Managing Multiple Backgrounds with Custom Properties appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Build a Chat App Using React Hooks in 100 Lines of Code

We’ve looked at React Hooks before, around here at CSS-Tricks. I have an article that introduces them as well that illustrates how to use them to create components through functions. Both articles are good high-level overviews about the way they work, but they open up a lot of possibilities, too.

So, that’s what we’re going to do in this article. We’re going to see how hooks make our development process easier and faster by building a chat application.

Specifically, we are building a chat application using Create React App. While doing so, we will be using a selection of React Hooks to simplify the development process and to remove a lot of boilerplate code that’s unnecessary for the work.

There are several open source Reacts hooks available and we’ll be putting those to use as well. These hooks can be directly consumed to build features that otherwise would have taken more of code to create. They also generally follow well-recognized standards for any functionality. In effect, this increases the efficiency of writing code and provides secure functionalities.

Let’s look at the requirements

The chat application we are going to build will have the following features:

  • Get a list of past messages sent from the server
  • Connect to a room for group chatting
  • Get updates when people disconnect from or connect to a room
  • Send and receive messages

We’re working with a few assumptions as we dive in:

  • We’ll consider the server we are going to use as a blackbox. Don’t worry about it working perfectly as we’re going to communicate with it using simple sockets.
  • All the styles are contained in a single CSS file, can be copied to the src directory. All the styles used within the app are linked in the repository.

Getting set up for work

OK, we’re going to want to get our development environment ready to start writing code. First off, React requires both Node and npm. You can set them up here.

Let’s spin up a new project from the Terminal:

npx create-react-app socket-client cd socket-client npm start

Now we should be able to navigate to http://localhost:3000 in the browser and get the default welcome page for the project.

From here, we’re going to break the work down by the hooks we’re using. This should help us understand the hooks as we put them into practical use.

Using the setState hook

The first hook we’re going to use is useState. It allows us to maintain state within our component as opposed to, say, having to write and initialize a class using this.state. Data that remains constant, such as username, is stored in useState variables. This ensures the data remains easily available while requiring a lot less code to write.

The main advantage of useState is that it’s automatically reflected in the rendered component whenever we update the state of the app. If we were to use regular variables, they wouldn’t be considered as the state of the component and would have to be passed as props to re-render the component. So, again, we’re cutting out a lot of work and streamlining things in the process.

The hook is built right into React, so we can import it with a single line:

import React, { useState } from 'react';

We are going to create a simple component that returns “Hello” if the user is already logged in or a login form if the user is logged out. We check the id variable for that.

Our form submissions will be handled by a function we’re creating called handleSubmit. It will check if the Name form field is completed. If it is, we will set the id and room values for that user. Otherwise, we’ll throw in a message reminding the user that the Name field is required in order to proceed.

// App.js  import React, { useState } from 'react'; import './index.css';  export default () => {   const [room, setRoom] = useState('');   const [id, setId] = useState('');    const handleSubmit = e => {     e.preventDefault();     const name = document.querySelector('#name').value.trim();     const room_value = document.querySelector('#room').value.trim();     if (!name) {       return alert("Name can't be empty");     }     setId(name);     setRoom(document.querySelector('#room').value.trim());   };    return id !== '' ? (     <div>Hello</div>   ) : (     <div style={{ textAlign: 'center', margin: '30vh auto', width: '70%' }}>       <form onSubmit={event => handleSubmit(event)}>         <input id="name" required placeholder="What is your name .." /><br />         <input id="room" placeholder="What is your room .." /><br />         <button type="submit">Submit</button>       </form>     </div>   ); };

That’s how we’re using the useState hook in our chat application. Again, we’re importing the hook from React, constructing values for the user’s ID and chat room location, setting those values if the user’s state is logged in, and returning a login form if the user is logged out.

Using the useSocket hook

We’re going to use an open source hook called useSocket to maintain a connection to our server. Unlike useState, this hook is not baked into React, so we’re going to have to add it to our project before importing it into the app.

npm add use-socket.io-client

The server connection is maintained by using the React Hooks version of the socket.io library, which is an easier way of maintaining websocket connections with a server. We are using it for sending and receiving real-time messages as well as maintaining events, like connecting to a room.

The default socket.io client library has global declarations, i.e., the socket variable we define can be used by any component. However, our data can be manipulated from anywhere and we won’t know where those changes are happening. Socket hooks counter this by constraining hook definitions at the component level, meaning each component is responsible for its own data transfer.

The basic usage for useSocket looks like this:

const [socket] = useSocket('socket-url')

We’re going to be using a few socket APIs as we move ahead. For the sake of reference, all of them are outlined in the socket.io documentation. But for now, let’s import the hook since we’ve already installed it.

import useSocket from 'use-socket.io-client';

Next, we’ve got to initialize the hook by connecting to our server. Then we’ll log the socket in the console to check if it is properly connected.

const [id, setId] = useState(''); const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');  socket.connect(); console.log(socket);

Open the browser console and the URL in that snippet should be logged.

Using the useImmer hook

Our chat app will make use of the useImmer hook to manage state of arrays and objects without mutating the original state. It combines useState and Immer to give immutable state management. This will be handy for managing lists of people who are online and messages that need to be displayed.

Using Immer with useState allows us to change an array or object by creating a new state from the current state while preventing mutations directly on the current state. This offers us more safety as far as leaving the current state intact while being able to manipulate state based on different conditions.

Again, we’re working with a hook that’s not built into React, so let’s import it into the project:

npm add use-immer

The basic usage is pretty straightforward. The first value in the constructor is the current state and the second value is the function that updates that state. The useImmer hook then takes the starting values for the current state.

const [data, setData] = useImmer(default_value)

Using the setData hook

Notice the setData hook in that last example? We’re using that to make a draft copy of the current data we can use to manipulate the data safely and use it as the next state when changes become immutable. Thus, our original data is preserved until we’re done running our functions and we’re absolutely clear to update the current data.

setData(draftState => {    draftState.operation();  });  // ...or  setData(draft => newState);  // Here, draftState is a copy of the current data

Using the useEffect hook

Alright, we’re back to a hook that’s built right into React. We’re going to use the useEffect hook to run a piece of code only when the application loads. This ensures that our code only runs once rather than every time the component re-renders with new data, which is good for performance.

All we need to do to start using the hook is to import it — no installation needed!

import React, { useState, useEffect } from 'react';

We will need a component that renders a message or an update based on the presence or absence of a sende ID in the array. Being the creative people we are, let’s call that component Messages.

const Messages = props => props.data.map(m => m[0] !== '' ?  (<li key={m[0]}><strong>{m[0]}</strong> : <div className="innermsg">{m[1]}</div></li>)  : (<li key={m[1]} className="update">{m[1]}</li>) );

Let’s put our socket logic inside useEffect so that we don’t duplicate the same set of messages repeatedly when a component re-renders. We will define our message hook in the component, connect to the socket, then set up listeners for new messages and updates in the useEffect hook itself. We will also set up update functions inside the listeners.

const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');       socket.connect();  const [messages, setMessages] = useImmer([]); useEffect(()=>{   socket.on('update', message => setMessages(draft => {     draft.push(['', message]);   }));    socket.on('message que',(nick, message) => {     setMessages(draft => {       draft.push([nick, message])     })   }); },0);

Another touch we’ll throw in for good measure is a “join” message if the username and room name are correct. This triggers the rest of the event listeners and we can receive past messages sent in that room along with any updates required.

// ...   setRoom(document.querySelector('#room').value.trim());   socket.emit('join', name, room); };  return id ? (   <section style={{display:'flex',flexDirection:'row'}} >     <ul id="messages"><Messages data={messages}></Messages></ul>     <ul id="online"> &#x1f310; :</ul>     <div id="sendform">       <form id="messageform" style={{display: 'flex'}}>         <input id="m" /><button type="submit">Send Message</button>       </form>     </div>   </section> ) : ( // ...

The finishing touches

We only have a few more tweaks to wrap up our chat app. Specifically, we still need:

  • A component to display people who are online
  • A useImmer hook for it with a socket listener
  • A message submission handler with appropriate sockets

All of this builds off of what we’ve already covered so far. I’m going to drop in the full code for the App.js file to show how everything fits together.

// App.js  import React, { useState, useEffect } from 'react';  import useSocket from 'use-socket.io-client';  import { useImmer } from 'use-immer';  import './index.css';  const Messages = props => props.data.map(m => m[0] !== '' ? (<li><strong>{m[0]}</strong> : <div className="innermsg">{m[1]}</div></li>) : (<li className="update">{m[1]}</li>) );  const Online = props => props.data.map(m => <li id={m[0]}>{m[1]}</li>);  export default () => {    const [room, setRoom] = useState('');    const [id, setId] = useState('');      const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');   socket.connect();    const [messages, setMessages] = useImmer([]);      const [online, setOnline] = useImmer([]);      useEffect(()=>{     socket.on('message que',(nick,message) => {       setMessages(draft => {         draft.push([nick,message])       })     });        socket.on('update',message => setMessages(draft => {       draft.push(['',message]);     }))        socket.on('people-list',people => {       let newState = [];       for(let person in people){         newState.push([people[person].id,people[person].nick]);       }       setOnline(draft=>{draft.push(...newState)});       console.log(online)     });        socket.on('add-person',(nick,id)=>{       setOnline(draft => {         draft.push([id,nick])       })     })        socket.on('remove-person',id=>{       setOnline(draft => draft.filter(m => m[0] !== id))     })        socket.on('chat message',(nick,message)=>{       setMessages(draft => {draft.push([nick,message])})     })   },0);      const handleSubmit = e => {     e.preventDefault();     const name = document.querySelector('#name').value.trim();       const room_value = document.querySelector('#room').value.trim();     if (!name) {       return alert("Name can't be empty");     }     setId(name);     setRoom(document.querySelector('#room').value.trim());     console.log(room)     socket.emit("join", name,room_value);   };      const handleSend = e => {     e.preventDefault();     const input = document.querySelector('#m');     if(input.value.trim() !== ''){       socket.emit('chat message',input.value,room);       input.value = '';     }   }      return id ? (     <section style={{display:'flex',flexDirection:'row'}} >       <ul id="messages"><Messages data={messages} /></ul>       <ul id="online"> &#x1f310; : <Online data={online} /> </ul>       <div id="sendform">         <form onSubmit={e => handleSend(e)} style={{display: 'flex'}}>             <input id="m" /><button style={{width:'75px'}} type="submit">Send</button>         </form>       </div>     </section>   ) : (     <div style={{ textAlign: 'center', margin: '30vh auto', width: '70%' }}>       <form onSubmit={event => handleSubmit(event)}>         <input id="name" required placeholder="What is your name .." /><br />         <input id="room" placeholder="What is your room .." /><br />         <button type="submit">Submit</button>       </form>     </div>   ); };

Wrapping up

That’s it! We built a fully functional group chat application together! How cool is that? The complete code for the project can be found here on GitHub.

What we’ve covered in this article is merely a glimpse of how React Hooks can boost your productivity and help you build powerful applications with powerful front-end tooling. I have built a more robust chat application in this comprehensive tutorial. Follow along if you want to level up further with React Hooks.

Now that you have hands-on experience with React Hooks, use your newly gained knowledge to get even more practice! Here are a few ideas of what you can build from here:

  • A blogging platform
  • Your own version of Instagram
  • A clone of Reddit

Have questions along the way? Leave a comment and let’s make awesome things together.

The post Build a Chat App Using React Hooks in 100 Lines of Code appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]