> All in One 586: December 2020

Ads

Thursday, December 31, 2020

Partly Cloudy today!



With a high of F and a low of 19F. Currently, it's 22F and Clear outside.

Current wind speeds: 8 from the Northwest

Pollen: 0

Sunrise: December 31, 2020 at 08:11PM

Sunset: January 1, 2021 at 05:39AM

UV index: 0

Humidity: 64%

via https://ift.tt/2livfew

January 1, 2021 at 10:07AM

Thank You (2020 Edition)

Heck of a year, eh? Like we do ever year, I’d like to give you a huge thanks for reading CSS-Tricks, and recap the year. More downs than ups, all told. Here at CSS-Tricks, it was more of a wash. Allow me to me share some numbers, milestones, and thoughts with you about our journey of 2020.

Let’s do the basic numbers

The site saw 94m pageviews this year. Last year we lost a smidge of pageviews (from 91m to 90m), so it’s nice to see that number go back up again, setting a new high record. Now I don’t have to tell myself stories like “jeez usage of browser extensions that block Google Analytics must be up.” Hitting 100m pageviews will be a nice milestone some future year. This number, long term, climbs very slowly. It’s a good reminder to me how much time, money, and energy are required to just maintain the traffic to a content site, let alone attempt to drive growth.

I have Cloudflare in front of the site this year. I think that’s a good idea generally, but especially now that they have specific technology to make it extra good. I’m a fan of pushing as much to the edge as we can, and now it’s not only static assets that are CDN-served but the content as well.

I mention that because now I have access to Cloudflare analytics, so I can compare across tools. I can’t see a whole year of data on Cloudflare, but comparing last month’s unique visitors between the two services, I see 6,847,979 unique visitors on Cloudflare compared to 6,125,272 sessions (or 7,449,921 unique page views — I’m not sure which is directly comparable) on Google Analytics. They seem pretty close. Closer than I thought they would be, since Google Analytics requires client-side JavaScript and Cloudflare analytics are, presumably, gathered at the DNS level, and thus not blockable like client-side JavaScript. I’ve turned off the WordPress-powered analytics for now, as having three places for analytics seemed a bit much, although I might flip them back on because, without them, I can’t see top on-site search results, which I definitely like to have.

Traffic that comes from organic¹ search was 77.7% this year, versus 80.6% last year. A 3% swing feels pretty large, yet almost entirely accounted for by a 3% swing from 9% to 12% in “direct” traffic. I have no idea what to make of that, but I suppose I don’t mind a better diversification in sources.

I find these end-of-year looks at the numbers sorta fun, but I’m generally not a big analytics guy. Last year I wrote:

There is a bunch of numbers I just don’t feel like looking at this year. We’ve traditionally done stuff like what countries people are from, what browsers they use (Chrome-dominant), mobile usage (weirdly low), and things like that. This year, I just don’t care. This is a website. It’s for everyone in the world that cares to read it, in whatever country they are in and whatever browser they want to.

I feel even more apathetic toward generalized analytics numbers this year. I think analytics are awesome when you have a question in mind you want an answer to, where the numbers help find that answer. Or for numbers that are obviously important and impactful to your site that you determined for yourself. Just poking around at numbers can fool you into thinking you’re gathering important insights and making considered decisions when you’re kinda just picking your nose.

One question that does interest me is what the most popular content is by traffic (we’ll get to that in a bit). Looking at the most popular content (by actual traffic) gives me a sense of what brings people here. Bringing traffic to the site is a goal. While we don’t generally sell sponsorship/advertising based on page views directly, those numbers matter to sponsors and fairly correlate directly to what we can charge.

Another bit of data I care about is what people search for that bring them to the site. Here’s how that breaks down:

  • Top 10: Various combinations of terms that have to do with flexbox and grid layout
  • Mixed into the top 20: Various alterations of the site’s name

From there, 10-100 are “random specific CSS things.” Beyond 100 is where SVG, JavaScript, design stuff, and CSS are sprinkled into the mix. The 251st ranked search term is the first time React shows up. The insight here is that: (1) our layout guides continue to do very well, (2) a lot of people like to get to the site first, then find what they need, and (3) searches for library-specific content isn’t a particularly common way to land here.

Top posts of the year

Thanks to Jacob, we can look at analytics data based on the year the content was written (and a few other bits of metadata).

Here’s an interesting thing. In 2019, articles written in 2019 generated about 6.3m page views. Those same articles, in 2020, generated 7.3m page views. Neat, huh? The articles drove more traffic as they aged.

Articles written in 2020 generated 12m pageviews. Here’s the top 10:

  1. CSS-Only Carousel
  2. Fluid Width Video (cheat, as this was written a few years ago as a stand-alone page, and I only moved it to the blog in 2020)
  3. How to Create an Animated Countdown Timer With HTML, CSS and JavaScript
  4. A Complete Guide to Links and Buttons
  5. The Hooks of React Router
  6. A Complete Guide to Dark Mode on the Web
  7. Neumorphism and CSS
  8. A Complete Guide to Data Attributes
  9. Why JavaScript is Eating HTML
  10. Front-End Challenges

Interesting backstory on that list. I dug into Google Analytics and created that Top 10 list based on the data it was showing me in a custom report, which Jacob taught me to do. Serendipitously, Jacob emailed me right after that to show me the Top 10 that he calculated, and it was slightly different than mine. Then I went back and re-ran my custom report, and it was slightly different than both the others. Weird! Jacob knew why. When you’re looking at a huge dataset like this in Google Analytics, they will only sample the data used for the report. It will show you a “yellow badge” and tell you what percentage of the data the report is based on. 500,000 sessions is the max, which is only 0.7% of what we needed to look at. That’s low enough that it accounted for the different lists. Jacob had already done some exporting and jiggering of the data to account for that, so the above list is what’s accurate to 100% of all sessions.

The top articles on the entire site from any year:

  1. The Complete Guide to Flexbox
  2. The Complete Guide to Grid
  3. Using SVG
  4. Perfect Full Page Background Images
  5. The Shapes of CSS
  6. Media Queries for Standard Devices
  7. Change Color of SVG on Hover
  8. CSS Triangle
  9. How to Scale SVG
  10. Using @font-face

Nothing from the Almanac made the top 10, but interestingly, right after that, the next 20 are so are heavily sprinkled with random articles from there. All told, the Almanac is about 14.8% of all traffic.

Two other things that I think are very cool that we did with content is:

  1. Published Jay Hoffman’s series on Web History, which include audio adaptations from Jeremy Keith that are served as a podcast.
  2. Published our end-of-year series like we did last year.

One of the many reasons I love being on WordPress is how easy it is to spin up series like these. All we did was toss up a category-specific template file and slapped on a little custom CSS. That gives the posts a cool landing page all to themselves, but are still part of the rest of the “flow” of the site (RSS, search, tags, etc.).

COVID

Perhaps the slight increase in traffic was COVID-related? With more people turning to coding as a good option for working from home, maybe there are more people searching for coding help. Who knows.

What we definitely found was that nearly every sponsor we work with, understandably, tightened their belt. Add in advertising plans with us that were reduced or canceled and, as a rough estimate, I’d say we’re down 25% in sponsorship revenue. That would be pretty devastating except for the fact that we try not to keep too many eggs in one basket.

Feels like a good time to mention that if your company is doing well and could benefit from reaching people who build websites, let’s talk sponsorship.

I’m trying to diversify revenue somewhat, even on this site alone. For example…

eCommerce

We’ve been using WooCommerce here to sell a couple of things.

Posters, mainly. A literal physical bit of printed paper sent through the post to you. The posters are unique designs made from the incredible illustrations that Lynn Fisher created for the flexbox and grid guides. We essentially “drop ship” them, meaning they are printed and shipped on-demand by another company. So, you buy them from this site, but another company takes it from there and does all the rest of it. That’s appealing because the amount of work is so low, but there are two major downsides: (1) Customer support for the orders becomes our problem and I’d say ~20% of orders need some kind of support, and (2) the profit margin is fairly slim compared to what it would be if we took on more of the work ourselves.

We also sell MVP Supporter memberships, which are great in that they don’t require much ongoing work. The trick there is just making sure it is valuable enough for people to buy, which we’ll have to work more on over time. For now, you basically get a book, video downloads, and no ads.

Loose math, eCommerce made up 5% of the lost revenue. Long way to go there, but it’s probably worth the journey as my guess is that this kind of revenue is less volatile.

I’m also still optimistic about Web Monetization in the long-term (here’s the last post I wrote on it). But right now, it is not a solution that makes for a significant new revenue stream. Optimistically, it’s something like 0.05% of revenue.

Social media

As far a website traffic driver goes, social media isn’t particularly huge at 2.2% of all traffic (down from 2.3% last year). That’s about where it always is, whether or not we put much effort into it over the course of a year, which is exactly why I try not to spend energy there. What little effort we do expend, 95% of it is toward Twitter. We lean on Jetpack’s social automation features, mostly. It is still cool to have @css as a handle, and we are closing in on half a million followers. You’d think that would be worth something. We’ll have to figure that out someday.

When we hand-write Tweets (rare), those are still the ones with the most potential. I only do that when it feels like something fun to do, because even though they can get the most engagement, the time/value thing still just doesn’t make it worthwhile.

Example hand-written tweet

Most of our tweets are just auto-generated when a new post is published. And we’ve been doing that for so long, I think that’s what the Twitter followers largely expect anyway, and that’s fine. We do have the ability to customize the Tweet before it goes out, which we try to, but usually don’t.

Example Auto-Generated Tweet

The other 5% of effort is Instagram just because it’s kinda fun. I don’t even wanna think about trying to extract direct value from Instagram. Maybe if we had a lot more products for direct sale it would make sense. But for now, just random tips and stuff to hold an audience.

Example Instagram Post

Screencasts

I did 22 screencasts this year. That’s a lot compared to the last many years! I’m not sure if I’ll be as ambitious in 2021, but I suspect I might be, because my setup at my desk is getting pretty good for doing them and my editing skills are slowly improving. I enjoy doing them, and it’s an occasional income stream (my favorite being pairing up with someone from a company and digging into their technology together). Plus, we got that cool new intro for our videos done by dina Amin.

The screencasts are published on the site and to iTunes as a videocast, but the primary place people watch is YouTube. I guess we could consider YouTube “social media” but I find that screencasts are more like “real content” in a way that I don’t with other social media. They are certainly much more time-consuming to produce and I hope more evergreen than a one-off tweet or something.

Newsletter

We hit 81,765 subscribers to the newsletter. On one hand, that’s a respectable number. On the other, it’s far too low considering how gosh darn good it is! I was hoping we’d hit 100k this year, but I didn’t actually do all that much to encourage signups, so that’s on me. I don’t think we missed a single week, so that’s a win, and considering we were at 65,000 last year, that’s still pretty good growth.

Comments

Y’all left 4,322 comments on the site this year. That’s down a touch from 4,710, but still decent averaging almost 12 a day.

I rollercoaster emotionally about comments. One day thinking they are too much trouble, requiring too much moderation and time to filter the junk. The vitriol can be so intense (on a site about code, wow) that some days I just wanna turn them off. Other times, I’m glad for the extra insight and corrections. Not to mention, hey, that’s content and content is good. We’ve never not had comments, so, hey, let’s keep ’em for now.

I absolutely always encourage your helpful, insightful, and kind comments, and promise to never publish rude or wrong comments (my call).

The forums completely shut down this year (into “read only” mode), so commenting activity from that didn’t exactly make its way over to the blog area. Closing the forums still feels… weird. I liked having a place to send (especially beginners) to ask questions. But, we just do not have the resources (or business model) to support safe and active forums. So closed they will remain, for now.

Goal review

  • 100k on email list. Fail on that one. That was kind of a moonshot anyway, and we never executed any sort of plan to help get there. For example, we could encourage it on social media more. We could attempt to buy ads elsewhere with a call to action to sign up. We could offer incentives to new subscribers somehow. We might do those things, or we might not. I don’t feel strongly enough right now to make it a goal for next year.
  • Two guides. We crushed this one. We published 9 guides. I consider this stuff our most valuable content. While I don’t want to only do this kind of content (because I think it’s fun to think of CSS-Tricks as a daily newspaper-style site as well), I want to put most of our effort here.
  • Have an obvious focus on how-to referential technical content. I think we did pretty good here. Having this in mind all the time both for ourselves and for guest posts meant making sure we were showcasing how to use tech and less focus on things like guest editorials which are, unfortunately, our least useful content. I’d like to be even stricter on this going forward. We’re so far along in our journey on this site. The expectation people have is that this site has answers for their technical front-end questions, so there is no reason not to lean entirely into that.
  • Get on Gutenberg. We also crushed it here. I think in the first month of the year I had us using Gutenberg on new content, and within a few months after that, we had Gutenberg enabled for all posts. It was work! And we still have a long way to go, as most posts on the site haven’t been “converted” into blocks, which is still not a brainless task. But, I consider it a fantastic success. I think Gutenberg is largely a damn pleasure to work with, making content authoring far more pleasurable, productive, and interesting.

New goals

  • Three guides. I know we did nine this year, but the goal was only two. I actually have ideas for three more, so I’ll make three the goal. Related side goal: I’d like to try to make mini-books out of some of these guides and either sell them individually or make it part of the MVP Supporter subscription.
  • Stay focused on how-to technical content around our strengths. Stuff like useful tips. Technical news with context. Advice on best-practices. I want to reign us in a bit more toward our strengths. HTML, CSS, and JavaScript stuff is high on that list of strengths, but not every framework, serverless technology, or build tool is. I’d like us to be more careful about not publishing things unless we can strongly vouch for it.
  • Complete all missing Almanac entries. There are a good 15-20 Almanac articles that could exist that don’t yet. Like we have place-items in there, but not place-content or place-self. Then there is esoteric stuff, like :current, :past, and :future time-dimensional pseudo-classes which, frankly, I don’t even really understand but are a thing. If you wanna help, please reach out.

Wrapping up

Thank you, again, for being a reader of this site. I hope these little peeks at our business somehow help yours. And I really hope 2021 is better than 2020, for all of us.

🧡

  1. I actually prefer my search grass-fed in addition to organic, but ok. ↩️

The post Thank You (2020 Edition) appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



from CSS-Tricks https://ift.tt/3rHRBmU
via IFTTT

A font-display setting for slow connections

Me, I really dislike FOUT. I like that it’s an option, because not displaying text quickly on the web is no good. I know font-display: swap; is popular because it’s good for performance, but that FOUT stuff pains me. Matt Hobbs:

If there’s one thing I’d like readers to take away from this post it’s that font-display: swap is a very good option for users with a fast internet connection. But its infinite swap period could be frustrating for users on very slow and unstable connections. If you have users viewing your site under these conditions (I’m pretty certain you will at some point in time), then it may be worth considering font-display: fallback or even font-display: optional.

Seeeee, I told ya. I like how font-display: optional; totally stops FOUT. The font is either applied super fast, or isn’t used at all (but still downloaded async). Chances are, on the next page load, the font is loaded and cached and will be used.

Note this is about slow connections, not necessarily connections where the user would prefer as little data usage as possible. If that’s the case, check out some of the recent posts we linked up in Responsible, Conditional Loading.


And boy howdy, the Web Performance Calendar this year was just loaded in great articles.

Direct Link to ArticlePermalink


The post A font-display setting for slow connections appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



from CSS-Tricks https://ift.tt/3lwMFx4
via IFTTT

3 Steps to Enable Client Hints on Your Image CDN

The goal of Client Hints is to provide a framework for a browser when informing the server about the context in which a web experience is provided.

HTTP Client Hints are a proposed set of HTTP Header Fields for proactive content negotiation in the Hypertext Transfer Protocol. The client can advertise information about itself through these fields so the server can determine which resources should be included in its response.

Wikipedia

With that information (or hints), the server can provide optimizations that help to improve the web experience, also known as Content Negotiation. For images, a better web experience means faster loading, less data payload, and a streamlined codebase.  

Client Hints have inherent value, but can be used together with  responsive images syntax to make responsive images less verbose and easier to maintain. With Client Hints, the server side, in this case an image CDN, can resize and optimize the image in real time.

Client Hints have been around for a while – since Chrome 35 in 2015, actually. However, support in most Chrome browsers got partly pulled due to privacy concerns in version 67. As a result, access to Client Hints was limited to certain Chrome versions on Android and first-party origins in other Chrome versions.

Now, finally, Google has enabled Client Hints by default for all devices in Chrome version 84!

Let’s see what’s required to make use of Client Hints.

1) Choose an Image CDN that Supports Client Hints

Not many image CDNs support client hints. Max Firtman did an extensive evaluation of Image CDNs that identified ones that supported client hints. ImageEngine stands out as the best image CDN with full Client Hints support in addition to more advanced features.

ImageEngine works like most CDNs by mapping the origin of the images, typically a web location or an S3 bucket, to a domain name pointing to the CDN address. Sign up for a free trial here. After signing up, trialers will get a dedicated ImageEngine delivery address that looks something like this: xxxzzz.cdn.imgeng.in. The ImageEngine delivery address can also be customized to one’s own domain by creating a CNAME DNS record. 

In the following examples, we will assume that ImageEngine is mapped to images.example.com in the DNS.

2) Make the Browser Send Client Hints

Now that the trialer has an ImageEngine account with full client hints support, we need to tell the browser to start sending the client hints to ImageEngine. This basically means that the webserver has to reply to a request with two specific HTTP headers. This  can be done manually on one’s website, or for example use a plugin if the site is running WordPress.

How the headers are added manually depends on one’s website:

  • A hosting provider or CDN probably offers a setting to alter http headers, 
  • One can add the headers in the code of their site. How this is done depends on the programming language or framework one is using. Try googling “add http headers <your programming language or framework>”
  • The hosting provider may run apache and allow users to edit the .htaccess configuration file. One can also add the headers in there.
  • Trialers can also add the headers to the markup inside the <head> element using the http-equiv meta element: <meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width">

Add Accept-CH header

The first header is the Accept-CH header. It tells the browser to start sending client hints:

Accept-CH: viewport-width, width, dpr

Add the Feature-Policy header

At the time of writing, the mechanism for delegating Client Hints to 3rd parties is named Feature Policies. However, it’s about to be renamed to Permission Policies.

Then, to make sure the Client Hints are sent along with the image requests to the ImageEngine delivery address obtained in step 1, this feature policy header must be added to server responses as well.

A Feature / Permission policy is a HTTP header specifying which origins (domains) have access to which browser features.

Feature-Policy: ch-viewport-width https://images.example.com;ch-width https://images.example.com;ch-dpr https://images.example.com;ch-device-memory https://images.example.com;ch-rtt https://images.example.com;ch-ect https://images.example.com;ch-downlink https://images.example.com

example.com must be replaced with the actual address refering to ImageEngine whether it’s the generic xxxzzz.cdn.imgeng.in-type or your customized delivery address.

Pitfall 1: Note the ch- prefix. The notation is ch– + client-hint name

Pitfall 2: Use lowercase! Even if docs and examples say, for example, Accept-CH: DPR, make sure to use ch-dpr in the policy header! 

Once the accept-ch and feature-policy header are set, the response from the server will look something like the screen capture above.

3) Set Sizes Attribute

Last, but not least, the <img> elements in the markup must be updated. 

Most important, the src of the <img> element must point to the ImageEngine delivery address. Make sure this is the same address used  in step 1 and mentioned in the feature-policy header in step 2.

Next, add the sizes attribute to the <img> elements. sizes is a part of the responsive images syntax which enable the browser to calculate the specific pixel size an image is displayed at. This size is sent to the image CDN in the width client hint.

<img src="https://images.example.com/test.jpg" sizes="200px" width="200px" alt="image">

If the width set in CSS or width attribute is known, one can “retrofit” responsive images by copying that value into sizes.

When these small changes have been made to the <img> element, the request to ImageEngine for images will contain the client hints like illustrated in the screen capture above. The “width” header tells ImageEngine the exact size the image needs to be to fit perfectly on the web page.

Enjoy Pixel-Perfect Images

Now, if tested in a supporting browser, like Chrome version 84 and below, the client hints should be flowing through to images.example.com

The <img> element is short and concise, and is rigged to provide even better adapted responsive images than a classic client-side implementation without client hints would. Less code, no need to produce multiple sizes of the images on your web server and the resource selection is still made by the browser but served by the image CDN. Best from both worlds!

Trialers can see the plumbing in action in this reference implementation on glitch.com. Make sure to test this in Chrome version 84 or newer!

By using an image CDN like ImageEngine that supports client hints, sites will never serve bigger images than necessary when the steps above are followed. Additionally, as a bonus, ImageEngine will also optimize and convert images between formats like WebP, JPEG2000 and MP4 in addition to the more common image formats.

Additionally, the examples above contain a few network- or connectivity-related Client Hints. ImageEngine may also optimize images according to this information.

What about browsers not supporting Client Hints? ImageEngine will still optimize and resize images thanks to advanced device detection at the CDN edge. This way, all devices and browsers will always get appropriately sized images.

ImageEngine offers a free trial, and anyone can sign up here to start implementing client hints on their website.


The post 3 Steps to Enable Client Hints on Your Image CDN appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



from CSS-Tricks https://ift.tt/3pIswXj
via IFTTT

Wednesday, December 30, 2020

Clear today!



With a high of F and a low of 15F. Currently, it's 22F and Clear outside.

Current wind speeds: 10 from the Southwest

Pollen: 0

Sunrise: December 30, 2020 at 08:10PM

Sunset: December 31, 2020 at 05:38AM

UV index: 0

Humidity: 59%

via https://ift.tt/2livfew

December 31, 2020 at 10:00AM

Daily Crunch: Amazon acquires Wondery

Amazon makes a big podcast acquisition, a Chinese robot maker raises $100 million and we review a robotic cat pillow. This is your Daily Crunch for December 30, 2020.

The big story: Amazon acquires Wondery

Amazon is the latest company to make a big acquisition in the podcast market — it’s buying Wondery, the podcast network behind shows like “Dirty John” and “Doctor Death.”

Although Wondery is becoming part of Amazon Music (which added podcast support in September), the company also says that “nothing will change for listeners” and that Wondery’s podcasts will continue to be available from “a variety of providers.”

The financial terms of the deal were not disclosed.

Startups, funding and venture capital

China’s adaptive robot maker Flexiv raises over $100M — Wang Shiquan, an alumnus of Stanford’s Biomimetics and Dexterous Manipulation Lab, founded Flexiv with a focus on building adaptive robots for the manufacturing industry.

Biteable raises a $7M Series A for its template-based online video builder — The product is designed for creating video assets that have more staying power than temporary social videos.

An earnest review of a robotic cat pillow — It’s cute!

Advice and analysis from Extra Crunch

On the diversity front, 2020 may prove a tipping point — VCs have talked about diversity for eons without doing much about it.

2020 will change the way we look at robotics — From logistics to food prep, robots are custom-built to help mankind survive a pandemic.

Dear Sophie: Tips for getting a National Interest green card by myself? — The latest edition of Dear Sophie, attorney Sophie Alcorn’s advice column answering immigration-related questions about working at technology companies.

(Extra Crunch is our membership program, which aims to democratize information about startups. You can sign up here for a holiday deal good through January 3. Read more about the deal here.)

Everything else

Section 230 is threatened in new bill tying liability shield repeal to $2,000 checks — The move seems more like a political maneuver than a real stab at tech regulation.

NSO used real people’s location data to pitch its contact-tracing tech, researchers say — Researchers say NSO’s use of real data “violated the privacy” of thousands of unwitting people.

The Daily Crunch is TechCrunch’s roundup of our biggest and most important stories. If you’d like to get this delivered to your inbox every day at around 3pm Pacific, you can subscribe here.



from Amazon – TechCrunch https://ift.tt/3pF3yYy
via IFTTT

CSS Individual Transform Properties in Safari Technology Preview

The WebKit blog details how to use individual CSS Transform properties in the latest version of Safari Technology Preview. This brings the browser in line with the CSS Transforms Module Level 2 spec, which breaks out the translate(), rotate() and scale() functions from the transform property into their own individual properties: translate, scale, and rotate.

So, instead of chaining those three functions on the transform property:

.some-element {
  transform: translate(50px 50px) rotate(15deg) scale(1.2);
}

…we can write those out individually as their own properties:

.some-element {
  translate(50px 50px);
  rotate(15deg);
  scale(1.2);
}

If you’re like me, your mind immediately jumps to “why the heck would we want to write MORE lines of code?” I mean, we’re used to seeing individual properties become sub-properties of a shorthand, not the other way around, like we’ve seen with background, border, font, margin, padding, place-items, and so on.

But the WebKit team outlines some solid reasons why we’d want to do this:

  • It’s simpler to write a single property when only one function is needed, like scale: 2; instead of transform: scale(2);.
  • There’s a lot less worry about accidentally overriding other transform properties when they’re chained together.
  • It’s a heckuva lot simpler to change a keyframe animation on an individual property rather than having to “pre-compute” and “recompute” intermediate values when chaining them with transform.
  • We get more refined control over the timing and keyframes of individual properties.

The post points out some helpful tips as well. Like, the new individual transform properties are applied first — translate, rotate, and scale, in that order — before the functions in the transform property.

Oh, and we can’t overlook browser support! It’s extremely limited at the time of writing… basically to just Safari Technology Preview 117 and Firefox 72 and above for a whopping 3.9% global support:

The post suggests using @supports if you want to start using the properties:

@supports (translate: 0) {
  /* Individual transform properties are supported */
  div {
    translate: 100px 100px;
  }
}

@supports not (translate: 0) {
  /* Individual transform properties are NOT supported */
  div {
    transform: translate(100px, 100px);
  }
}

That’s the code example pulled straight from the post. Modifying this can help us avoid using the not operator. I’m not sure that’s an improvement to the code or not, but it feels more like progressive enhancement to do something like this:

div {
  transform: translate(100px, 100px);
}

@supports (translate: 0) {
  /* Individual transform properties are supported */
  div {
    transform: none;
    translate: 100px 100px;
  }
}

That way, we clear the shorthand functions and make way for the individual properties, but only if they’re supported.


The post CSS Individual Transform Properties in Safari Technology Preview appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



from CSS-Tricks https://ift.tt/2WWkn51
via IFTTT

Amazon acquires podcast network Wondery

Amazon just announced that it’s acquiring Wondery, the network behind podcasts including “Dirty John” and “Dr. Death.”

Wondery will become part of Amazon Music, which added support for podcasts (including its own original shows) in September. At the same time, the announcement claims that “nothing will change for listeners” and that the network’s podcasts will continue to be available from “a variety of providers.”

Media companies and streaming audio platforms are all making big bets on podcasting, with Spotify making a series of acquisitions including podcast network Gimlet, SiriusXM acquiring Stitcher and The New York Times acquiring Serial Productions. Amazon is coming relatively late to this market, but it will now have the support of a popular podcast maker as it works to catch up.

“With Amazon Music, Wondery will be able to provide even more high-quality, innovative content and continue their mission of bringing a world of entertainment and knowledge to their audiences, wherever they listen,” Amazon wrote.

Financial terms were not disclosed. The Wall Street Journal previously reported that acquisition talks were in the works, and that those talks valued Wondery at around $300 million.

The startup was founded in 2016 by former Fox executive Hernan Lopez (who’s currently fighting federal corruption charges tied to his time at Fox). Numbers from Podtrac rank it as the fourth largest podcast publisher in November, with an audience in the U.S. of more than 9 million unique listeners.

Wondery has raised a total of $15 million in funding from Advancit Capital, BDMI, Greycroft, Lerer Hippeau and others, according to Crunchbase.

 



from Amazon – TechCrunch https://ift.tt/38Mypfc
via IFTTT

Cloudinary Tricks for Video

Creating video is time consuming. A well-made 5-minute video can take hours to plan, record, and edit — and that’s before we start talking about making that video consistent with all the other videos on your site.

When we took on the Jamstack Explorers project (a video-driven educational resource for web developers), we wanted to find the right balance of quality and shipping: what could we automate in our video production process to reduce the time and number of steps required to create video content without sacrificing quality?

With the help of Cloudinary, we were able to deliver a consistent branding approach in all our video content without adding a bunch of extra editing tasks for folks creating videos. And, as a bonus, if we update our branding in the future, we can update all the video branding across the whole site at once — no video editing required!

What does “video branding” mean?

To make every video on the Explorers site feel like it all fits together, we include a few common pieces in each video:

  1. A title scene
  2. A short intro bumper (video clip) that shows the Jamstack Explorers branding
  3. A short outro bumper that either counts down to the next video or shows a “mission accomplished” if this is the last video in the mission

Skip to the end: here’s how a branded video looks

To show the impact of adding the branding, here’s one of the videos from Jamstack Explorers without any branding:

This video (and this Vue mission from Ben Hong) is legitimately outstanding! However, it starts and ends a little abruptly, and we don’t have a sense of where this video lives.

We worked with Adam Hald to create branded video assets that help give each video a sense of place. Check out the same video with all the Explorers branding applied:

We get the same great content, but now we’ve added a little extra va-va-voom that makes this feel like it’s part of a larger story.

In this article, we’ll walk through how we automatically customize every video using Cloudinary.

How does Cloudinary make this possible?

Cloudinary is a cloud-based asset delivery network that gives us a powerful, URL-based API to manipulate and transform media. It supports all sorts of asset types, but where it really shines is with images and video.

To use Cloudinary, you create a free account, then upload your asset. This asset then becomes available at a Cloudinary URL:

https://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg
                           ^^^^^^^             ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
                              |                      |                |
                              V                      V                V
                      cloud (account) name    version (optional)  file name

This URL points to the original image and can be used in <img /> tags and other markup.

The original image size is 97.6kB.

Dynamically adjust file format and quality to reduce file sizes

If we’re using this image on a website and want to improve our site performance, we may decide to reduce the size of this image by using next-generation formats like WebP, AVIF, and so on. These new formats are much smaller, but aren’t supported by all browsers, which would usually mean using a tool to generate multiple versions of this image in different formats, then using a <picture> element or other specialized markup to provide modern options with the JPG fallback for older browsers.

With Cloudinary, all we have to do is add a transformation to the URL:

https://res.cloudinary.com/netlify/image/upload/q_auto,f_auto/v1605632851/explorers/avatar.jpg
                                                ^^^^^^^^^^^^
                                                      |
                                                      V
                                    automatic quality & format transformations

What we see in the browser is visually identical:

The transformed image is 15.4kB.

By setting the file format and quality settings to automatic (f_auto,q_auto), Cloudinary is able to detect which formats are supported by the client and serves the most efficient format at a reasonable quality level. In Chrome, for example, this image transforms from a 97.6kB JPG to a 15.4kB WebP, and all we had to do was add a couple of things to the URL!

We can transform our images in lots of different ways!

We can go further with other transformations, including resizing (w_150 for “resize to 150px wide”) and color effects (e_grayscale for “apply the grayscale effect”):

https://res.cloudinary.com/netlify/image/upload/q_auto,f_auto,w_150,e_grayscale/v1605632851/explorers/avatar.jpg
The same image after adding grayscale effects and resizing.

This is only a tiny taste of what’s possible — make sure to check out the Cloudinary docs for more examples!

There’s a Node SDK to make this a little more human-readable

For more advanced transformations like what we’re going to get into, writing the URLs by hand can get a little hard to read. We ended up using the Cloudinary Node SDK to give us the ability to add comments and explain what each transformation was doing, and that’s been extremely helpful as we maintain and evolve the platform.

To install it, get your Cloudinary API key and secret from your console, then install the SDK using npm:

# create a new directory
mkdir cloudinary-video

# move into the new directory
cd cloudinary-video/

# initialize a new Node project
npm init -y

# install the Cloudinary Node SDK
npm install cloudinary

Next, create a new file called index.js and initialize the SDK with your cloud_name and API credentials:

const cloudinary = require('cloudinary').v2;

// TODO replace these values with your own Cloudinary credentials
cloudinary.config({
  cloud_name: 'your_cloud_name',
  api_key: 'your_api_key',
  api_secret: 'your_api_secret',
});

Don’t commit your API credentials to GitHub or share them anywhere. Use environment variables to keep them safe! If you’re unfamiliar with environment variables, Colby Fayock has written a great introduction to using environment variables.

Next, we can create the same transformation as before using slightly more human-readable configuration settings:

cloudinary.uploader
  // the first argument should be the public ID (including folders!) of the
  // image we want to transform
  .explicit('explorers/avatar', {
    // these two properties match the beginning of the URL:
    // https://res.cloudinary.com/netlify/image/upload/...
    //                                    ^^^^^^^^^^^^
    resource_type: 'image',
    type: 'upload',

    // "eager" means we want to run these transformations ahead of time to avoid
    // a slow first load time
    eager: [
      {
        fetch_format: 'auto',
        quality: 'auto',
        width: 150,
        effect: 'grayscale',
      },
    ],

    // allow this transformed image to be cached to avoid re-running the same
    // transformations over and over again
    overwrite: false,
  })
  .then((result) => {
    console.log(result);
  });

Let’s run this code by typing node index.js in our terminal. The output will look something like this:

{
  asset_id: 'fca4abba96ffdf70ef89498aa340ae4e',
  public_id: 'explorers/avatar',
  version: 1605632851,
  version_id: 'b8a923931af20404e89d03852ff1bff1',
  signature: 'e7201c9ab36cb5b6a0545cee4f5f8ee27fb7f99f',
  width: 300,
  height: 300,
  format: 'jpg',
  resource_type: 'image',
  created_at: '2020-11-17T17:07:31Z',
  bytes: 97633,
  type: 'upload',
  url: 'http://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg',
  secure_url: 'https://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg',
  access_mode: 'public',
  eager: [
    {
      transformation: 'e_grayscale,f_auto,q_auto,w_150',
      width: 150,
      height: 150,
      bytes: 6192,
      format: 'jpg',
      url: 'http://res.cloudinary.com/netlify/image/upload/e_grayscale,f_auto,q_auto,w_150/v1605632851/explorers/avatar.jpg',
      secure_url: 'https://res.cloudinary.com/netlify/image/upload/e_grayscale,f_auto,q_auto,w_150/v1605632851/explorers/avatar.jpg'
    }
  ]
}

Under the eager property, our transformations are shown along with the full URL to view the transformed image.

While the Node SDK is probably overkill for a straightforward transformation like this one, it becomes really handy when we start looking at the complex transformations required to add video branding.

Transforming videos with Cloudinary

To transform our videos in Jamstack Explorers, we follow the same approach: each video is uploaded to Cloudinary, and then we modify the URLs to resize, adjust quality, and insert the title card and bumpers.

There are a few major categories of transformation that we’ll be tackling to add the branding:

  1. Overlays
  2. Transitions
  3. Text overlays
  4. Splicing

Let’s look at each of these categories and see if we can’t reimplement the Jamstack Explorers branding on Ben’s video! Let’s get set up by setting up index.js to transform our base video:

cloudinary.uploader
  .explicit('explorers/bumper', {
    // these two properties match the beginning of the URL:
    // https://res.cloudinary.com/netlify/image/upload/...
    //                                    ^^^^^^^^^^^^
    resource_type: 'video',
   type: 'upload',

    // "eager" means we want to run these transformations ahead of time to avoid
    // a slow first load time
    eager: [
      {
        fetch_format: 'auto',
        quality: 'auto',
        height: 360,
        width: 640,
        crop: 'fill', // avoid letterboxing if videos are different sizes
      },
    ],

    // allow this transformed image to be cached to avoid re-running the same
    // transformations over and over again
    overwrite: false,
  })
  .then((result) => {
    console.log(result);
  });

You may have noticed that we’re using a video called “bumper” instead of Ben’s original video. This is due to the way Cloudinary orders videos as we add them together. We’ll add Ben’s video in the next section!

Combine two videos with a custom transition using Cloudinary

To add our bumpers, we need to add a second transformation “layer” to the eager array that adds a second video as an overlay.

To do this, we use the overlay transformation and set it to video:publicID, where publicID is the Cloudinary public ID of the asset with any slashes (/) transformed to colons (:).

We also need to tell Cloudinary how to transition between the two videos, which we do using a special kind of video called a luma matte that lets us mask one video with the black area of the video, and a second video with the white area. This results in a stylized cross-fade.

Here’s what the luma matte looks like on its own:

The video and the transition both have their own transformations, which means that we need to treat them as different “layers” in the Cloudinary transform. This means splitting them into separate objects, then adding additional objects to “apply” each layer, which allows us to call that section done and continue adding more transformations to the main video.

To tell Cloudinary this this is a luma matte and not another video, we set the effect type to transition.

Make the following changes in index.js to put all of this in place:

const videoBaseTransformations = {
  fetch_format: 'auto',
  quality: 'auto',
  height: 360,
  width: 600,
  crop: 'fill',
}

cloudinary.uploader
  .explicit('explorers/bumper', {
    // these two properties match the beginning of the URL:
    // <https://res.cloudinary.com/netlify/image/upload/>...
    //
    resource_type: 'video',
    type: 'upload',

    // "eager" means we want to run these transformations ahead of time to avoid
    // a slow first load time
    eager: [
      videoBaseTransformations,
      {
        overlay: 'video:explorers:LCA-07-lifecycle-hooks',
        ...videoBaseTransformations,
      },
      {
        overlay: 'video:explorers:transition',
        effect: 'transition',
      },
      { flags: 'layer_apply' }, // <= apply the transformation
      { flags: 'layer_apply' }, // <= apply the actual video
    ],

    // allow this transformed image to be cached to avoid re-running the same
    // transformations over and over again
    overwrite: false,
  })
  .then((result) => {
    console.log(result);
  });

We need the same format, quality, and sizing transformations on all videos, so we pulled those out into a variable called videoBaseTransformations, then added a second object to contain the overlay.

If we run this with node index.js, the video we get back looks like this:

Not bad! This already looks like it’s part of the Jamstack Explorers site, and that transition adds a nice flow from the common bumper into the custom video.

Adding the outro bumper works exactly the same: we need to add another overlay for the ending bumper and a transition. We won’t show this code in the tutorial, but you can see it in the source code if you’re interested.

Add a title card to a video using text overlays

To add a title card, there are two distinct steps:

  1. Extract a short video clip to serve as the title card background
  2. Add a text overlay with the video’s title

The next two sections walk through each step individually so we can see the distinction between the two.

Extract a short video clip to use as the title card background

When Adam Hald created the Explorers video assets, he included a beautiful intro video that opens on a starry sky that’s perfect for a title card. Using Cloudinary, we can grab a few seconds of that starry sky and splice it into every video as a title card!

In index.js, add the following transformation blocks:

cloudinary.uploader
  .explicit('explorers/bumper', {
    // these two properties match the beginning of the URL:
    // https://res.cloudinary.com/netlify/image/upload/...
    //
    resource_type: 'video',
    type: 'upload',

    // "eager" means we want to run these transformations ahead of time to avoid
    // a slow first load time
    eager: [
      videoBaseTransformations,
      {
        overlay: 'video:explorers:LCA-07-lifecycle-hooks',
        ...videoBaseTransformations,
      },
      {
        overlay: 'video:explorers:transition',
        effect: 'transition',
      },
      { flags: 'layer_apply' }, // <= apply the transformation
      { flags: 'layer_apply' }, // <= apply the actual video

      // add the outro bumper and a transition
      {
        overlay: 'video:explorers:countdown',
        ...videoBaseTransformations,
      },
      {
        overlay: 'video:explorers:transition',
        effect: 'transition',
      },
      { flags: 'layer_apply' },
      { flags: 'layer_apply' },

      // splice a title card at the beginning of the video
      {
        overlay: 'video:explorers:intro',
        flags: 'splice', // splice this into the video
        ...videoBaseTransformations,
      },
      {
        audio_codec: 'none', // remove the audio
        end_offset: 3, // shorten to 3 seconds
        effect: 'accelerate:-25', // slow down 25% (to ~4 seconds)
      },
      {
        flags: 'layer_apply',
        start_offset: 0, // put this at the beginning of the video
      },
    ],

    // allow this transformed image to be cached to avoid re-running the same
    // transformations over and over again
    overwrite: false,
  })
  .then((result) => {
    console.log(result);
  });

Using the splice flag, we tell Cloudinary to add this video directly without a transition.

In the next set of transformations, we add three transformations we haven’t seen before:

  1. We set audio_codec to none to remove sound from this segment of video.
  2. We set end_offset to 3, which means we’ll get only the first 3 seconds of the video.
  3. We add the accelerate effect with a value of -25, which slows the video down by 25%.

Running node index.js will now give us a video that starts with just under 4 seconds of silent, starry skies:

Add text overlays to videos using Cloudinary

Our last step is to add a text overlay to show the video title!

Text overlays use the same overlay property as other overlays, but we pass an object with settings for the font. Cloudinary supports a wide variety of fonts — I haven’t been able to find a definitive list, but it seems to be a large number of Google Fonts — and if you’ve purchased a license to use a custom font, you can upload a custom font to Cloudinary for use in text overlays as well.

cloudinary.uploader
  .explicit('explorers/bumper', {
    // these two properties match the beginning of the URL:
    // <https://res.cloudinary.com/netlify/image/upload/>...
    //
    resource_type: 'video',
    type: 'upload',

    // "eager" means we want to run these transformations ahead of time to avoid
    // a slow first load time
    eager: [
      videoBaseTransformations,
      {
        overlay: 'video:explorers:LCA-07-lifecycle-hooks',
        ...videoBaseTransformations,
      },
      {
        overlay: 'video:explorers:transition',
        effect: 'transition',
      },
      { flags: 'layer_apply' }, // <= apply the transformation
      { flags: 'layer_apply' }, // <= apply the actual video

      // add the outro bumper and a transition
      {
        overlay: 'video:explorers:countdown',
        ...videoBaseTransformations,
      },
      {
        overlay: 'video:explorers:transition',
          effect: 'transition',
        },
        { flags: 'layer_apply' },
        { flags: 'layer_apply' },

        // splice a title card at the beginning of the video
        {
          overlay: 'video:explorers:intro',
          flags: 'splice', // splice this into the video
          ...videoBaseTransformations,
        },
        {
          audio_codec: 'none', // remove the audio
          end_offset: 3, // shorten to 3 seconds
          effect: 'accelerate:-25', // slow down 25% (to ~4 seconds)
        },
        {
        overlay: {
          font_family: 'roboto', // lots of Google Fonts are supported
          font_size: 40,
          text_align: 'center',
          text: 'Lifecycle Hooks', // this can be any text you want
        },
        width: 500,
        crop: 'fit',
        color: 'white',
      },
      { flags: 'layer_apply' },
      {
        flags: 'layer_apply',
        start_offset: 0, // put this at the beginning of the video
      },
    ],

    // allow this transformed image to be cached to avoid re-running the same
    // transformations over and over again
    overwrite: false,
  })
  .then((result) => {
    console.log(result);
  });

In addition to setting the font size and alignment, we also apply a width of 500px (which will be centered by default) to keep our title text from smashing into the side of the title card, and set the crop value to fit, which will wrap longer titles. Setting the color to white makes our text visible against the dark, starry background.

Run node index.js to generate the URL and we’ll see our fully branded video, including a title card and bumpers!

Build your video branding once; use it everywhere

Creating bumpers, transitions, and title cards is a lot of work. Creating high-quality video content is also a lot of work. If we had to manually edit every Jamstack Explorers video to insert these title cards and bumpers, it’s extremely unlikely that we would have actually done it.

We knew that the only realistic way for us to keep the videos consistently branded was to reduce the friction of adding the branding, and Cloudinary let us automate it entirely. This means that we can stay consistent without any manual steps!

As an added bonus, it also means that if we update our title cards or bumpers in the future, we can update all the branding for all the videos by changing the code in one place. This is a huge relief for us, because we know that Explorers is going to continue to grow and evolve over time.

What to do next

Now that you know how to use Cloudinary to add custom branding, here are some additional resources to help you keep learning.

What else can you automate using Cloudinary? How much time could you save by automating the repetitive parts of your video editing workflow? I am exactly the kind of nerd who loves to talk about this stuff, so send me your ideas on Twitter!


The post Cloudinary Tricks for Video appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



from CSS-Tricks https://ift.tt/3o4PFmf
via IFTTT

The Rules of Margin Collapse

Josh Comeau covers the concept of margin collapsing:

This idea might sound simple, but if you’ve been writing CSS for a while, you’ve almost certainly been surprised when margins either don’t collapse, or they collapse in weird and unexpected ways. In real-world projects, all kinds of circumstances can complicate matters.

The basic stuff to know:

  • Margin collapsing only happens in the block-direction. This is true even if you change the writing-mode or use logical properties.
  • The largest margin “wins”
  • Any element in between will nix the collapsing (if we’re talking within-parent collapsing, even a bit of padding or border will be the in-between thing and prevent the collapsing, as Geoff noted when he covered it).

But it gets way weirder:

  • Margins can collapse even when they aren’t from sibling elements.
  • Margins in the same direction from different elements can also collapse.
  • Margins from any number of elements can collapse.
  • Negative margins also collapse, but it’s the larger-negative number that wins.
  • If it’s a bunch of elements all with different margins, you have to basically learn an algorithm to understand what happens and why.

It’s unfortunate that those things happen at all. It can be frustrating for any skill level. These are quirks of CSS that that have to be taught explicitly, rather than feeling like a natural part of a system. Even the CSS working group considers it a mistake:

The top and bottom margins of a single box should never have been allowed to collapse together automatically as this is the root of all margin-collapsing evil.

😬

I don’t know that margin collapsing causes epic troubles in day-to-day CSSin’, but you gotta admit this is messy at best.


I also think about how it was a thing this year to suggest centering content via CSS grid and plopping all elements into the middle of a three-column grid ala .grid-wrapper > * { grid-column: 2; }. The point being that you still have the full grid to work with, so it’s easier to make one-off elements go full-bleed, edge-to-edge (or otherwise use the space). But when you do that, the elements become grid items and are out of the normal flow, so you won’t get margin collapsing. That used to feel like a strike against this technique, at least to me, since it would be unexpected. But thinking now about how janky margin collapsing is, maybe the avoiding of margin collapsing is yet another advantage of this sort of technique.

Direct Link to ArticlePermalink


The post The Rules of Margin Collapse appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



from CSS-Tricks https://ift.tt/36Ws2q5
via IFTTT

Mostly Clear today!

With a high of F and a low of 15F. Currently, it's 14F and Clear outside. Current wind speeds: 13 from the Southwest Pollen: 0 S...