Front End Web Development

Radio Buttons Are Like Selects; Checkboxes Are Like Multiple Selects

Css Tricks - Wed, 05/20/2020 - 4:41am

I was reading Anna Kaley’s “Listboxes vs. Dropdown Lists” post the other day. It’s a fairly straightforward comparison between different UI implementations of selecting options. There is lots of good advice there. Classics like that you should use radio buttons (single select) or checkboxes (multiple select) if you’re showing five or fewer options, and the different options when the number of options grows from there.

One thing that isn’t talked about is how you implement these things. I imagine that’s somewhat on purpose as the point is to talk UX, not tech. But how you implement them plays a huge part in UX. In web design and development circles, the conversation about these things usually involves whether you can pull these things off with native controls, or if you need to rebuild them from scratch. If you can use native controls, you often should, because there are tons of UX that you get for free that that might otherwise be lost or forgotten when you rebuild — like how everything works via the keyboard.

The reason people chose “rebuild” is often for styling reasons, but that’s changing slowly over time. We’ve got lots of control over radios and checkboxes now. We can style the outside of selects pretty well and even the inside with trickery.

But even without custom styling, we still have some UI options. If you need to select one option from many, we’ve got <input type="radio"> buttons, but data and end-result-wise, that’s the same as a <select>. If you need to select multiple options, we’ve got <input type="checkbox">, but that’s data and end-result-wise the same as <select multiple>.

CodePen Embed Fallback

You pick based on the room you have available and the UX of whatever you’re building.

The post Radio Buttons Are Like Selects; Checkboxes Are Like Multiple Selects appeared first on CSS-Tricks.

WordPress Block Transforms

Css Tricks - Tue, 05/19/2020 - 1:52pm

This has been the year of Gutenberg for us here at CSS-Tricks. In fact, that’s a goal we set at the end of last year. We’re much further along that I thought we’d be, authoring all new content in the block editor¹, enabling the block editor for all content now. That means when we open most old posts, we see all the content in the “Classic” block. It looks like this:

A post written on CSS-Tricks before we were using the block editor.

The entire contents of the post is in a single block, so not exactly making any use of the block editor. It’s still “visual,” like the block editor, but it’s more like the old visual editor using TinyMCE. I never used that as it kinda forcefully mangled HTML in a way I didn’t like.

This is the #1 thing I was worried about

Transforming a Classic block into new blocks is as trivial as selecting the Classic block and selecting the “Convert to Blocks” option.

Select the option and the one block becomes many blocks.

How does the block editor handle block-izing old content, when we tell it to do that from the “Convert to Blocks” option? What if it totally screws up content during the conversion? Will we ever be able to switch?

The answer: it does a pretty darn good job. But… there are still issues. Not “bugs” but situations where we have custom HTML in our old content and it doesn’t know what to do with it — let alone how to convert it into exactly the blocks we wish it would. There is a way!

Basic Block Transforms

That’s where this idea of “Block Transforms” comes in. All (well, most?) native blocks have “to” and “from” transformations. You’re probably already familiar with how it manifests in the UI. Like a paragraph can transform “to” a quote and vice versa. Here’s a super meta screenshot of this very paragraph:

Those transforms aren’t magic; they are very explicitly coded. When you register a block, you specify the transforms. Say you were registering your own custom code block. You’d want to make sure that you could transform it…

  • From and to the default built-in code block, and probably a handful of others that might be useful.
  • Back to the built-in code block.

Which might look like:

registerBlockType("my/code-block", { title: __("My Code Block"), ... transforms: { from: [ { type: "block", priority: 7, blocks: ["core/code", "core/paragraph", "core/preformatted"], transform: function (attributes) { return createBlock("my/code-block", { content: attributes.content, }); }, }, ], to: [ { type: "block", blocks: ["core/code"], transform: ({ content }) => createBlock("core/code", { content }), }, ], ...

Those are transforms to and from other blocks. Fortunately, this is a pretty simple block where we’re just shuffling the content around. More complex blocks might need to pass around more data, but I haven’t had to deal with that yet.

The more magical stuff: Block Transforms from raw code

Here’s the moment of truth for old content:

The “Convert to Blocks” option.

In this situation, blocks are being created not from other blocks, but from raw code. Quite literally, the HTML is being looked at and choices are being made about what blocks to make from chunks of that HTML. This is where it’s amazing the block editor does such a good job with the choices, and also where things can go wrong and it can fail, make wrong block choices, or mangle content.

In our old content, a block of code (a super very important thing) in a post would look like this:

<pre rel="JavaScript"><code class="language-javascript" markup="tt"> let html = `<div>cool</div>`; </code></pre>

Sometimes the block conversion would do OK on those, turning it into a native code block. But there were a number of problems:

  1. I don’t want a native code block. I want that to be transformed into our own new code block (blogged about that here).
  2. I need some of the information in those attributes to inform settings on the new block, like what kind of code it is.
  3. The HTML in our old code blocks was not escaped and I need it to not choke on that.

I don’t have all the answers here, as this is an evolving process, but I do have some block transforms in place now that are working pretty well. Here’s what a “raw” transform (as opposed to a “block” transform) looks like:

registerBlockType("my/code-block", { title: __("My Code Block"), // ... transforms: { from: [ { type: "block", priority: 7, // ... }, { type: "raw", priority: 8, isMatch: (node) => node.nodeName === "PRE" && node.children.length === 1 && node.firstChild.nodeName === "CODE", transform: function (node) { let pre = node; let code = node.querySelector("code"); let codeType = "html"; if (pre.classList.contains("language-css")) { codeType = "css"; } if (pre.getAttribute("rel") === "CSS") { codeType = "css"; } if (pre.classList.contains("language-javascript")) { codeType = "javascript"; } if (code.classList.contains("language-javascript")) { codeType = "javascript"; } // ... other data wrangling... return createBlock("csstricks/code-block", { content: code.innerHTML, codeType: codeType, }); }, }, ], to: [ // ... ], // ... }

That isMatch function runs on every node in the HTML it finds, so this is the big opportunity to return true from that in the special situations you need to. Note in the code above that I’m specifically looking for HTML that looks like <pre ...><code ...>. When that matches, the transform runs, and I can return a createBlock call that passes in data and content I extract from the node with JavaScript.

Another example: Pasting a URL

“Raw” transforms don’t only happen when you “Convert to Blocks.” They happen when you paste content into the block editor too. You’ve probably experienced this before. Say you have copied some table markup from somewhere and paste it into the block editor -— it will probably paste as a table. A YouTube URL might paste into an embed. This kind of thing is why copy/pasting from Word documents and the like tend to work so well with the block editor.

Say you want some special behavior when a certain type of URL is pasted into the editor. This was the situation I was in with our custom CodePen Embed block. I wanted it so if you pasted a codepen.io URL, it would use this custom block, instead of the default embed.

This is a “from” transform that looks like this:

{ type: "raw", priority: 8, // higher number to beat out default isMatch: (node) => node.nodeName === "P" && node.innerText.startsWith("https://codepen.io/"), transform: function (node) { return createBlock("cp/codepen-gutenberg-embed-block", { penURL: node.innerText, penID: getPenID(node.innerText), // helper function }); }, } So…

Is it messy? A little. But it’s as powerful as you need it to be. If you have an old site with lots of bespoke HTML and shortcodes and stuff, then getting into block transforms is the only ticket out.

I’m glad I went to WPBlockTalk and caught K. Adam White’s talk on shortcodes because there was just one slide that clued me into that this was even possible. There is a little bit of documentation on it.

One thing I’d like to figure out is if it’s possible to run these transforms on all old content in the database. Seems a little scary, but also like it might be a good idea in some situations. Once I get my transformations really solid, I could see doing that so any old content ready-to-go in the block editor when opening it up. I just have no idea how to go about it.

I’m glad to be somewhat on top of this though, as I friggin love the block editor right now. It’s a pleasure to write in and build content with it. I like what Justin Tadlock said:

The block system is not going anywhere. WordPress has moved beyond the point where we should consider the block editor as a separate entity. It is an integral part of WordPress and will eventually touch more and more areas outside of the editing screen.

It’s here to stay. Embracing the block editor and bending it to our will is key.

  1. What are we calling it anyway? “Gutenberg” doesn’t seem right anymore. Feels like that will fade away, even though the development of it still happens in the Gutenberg plugin. I think I’ll just call it “the block editor” unless specifically referring to that plugin.

The post WordPress Block Transforms appeared first on CSS-Tricks.

How to Build a Chrome Extension

Css Tricks - Tue, 05/19/2020 - 4:38am

I made a Chrome extension this weekend because I found I was doing the same task over and over and wanted to automate it. Plus, I’m a nerd living through a pandemic, so I spend my pent-up energy building things. I’ve made a few Chrome Extensions over the years, hope this post helps you get going, too. Let’s get started!

Create the manifest

The first step is creating a manifest.json file in a project folder. This serves a similar purpose to a package.json, it provides the Chrome Web Store with critical information about the project, including the name, version, the required permissions, and so forth. Here’s an example:

{ "manifest_version": 2, "name": "Sample Name", "version": "1.0.0", "description": "This is a sample description", "short_name": "Short Sample Name", "permissions": ["activeTab", "declarativeContent", "storage", "<all_urls>"], "content_scripts": [ { "matches": ["<all_urls>"], "css": ["background.css"], "js": ["background.js"] } ], "browser_action": { "default_title": "Does a thing when you do a thing", "default_popup": "popup.html", "default_icon": { "16": "icons/icon16.png", "32": "icons/icon32.png" } } }

You might notice a few things- first: the names and descriptions can be anything you’d like.

The permissions depend on what the extension needs to do. We have ["activeTab", "declarativeContent", "storage", "<all_urls>"] in this example because this particular extension needs information about the active tab, needs to change the page content, needs to access localStorage, and needs to be active on all sites. If it only needs it to be active on one site at a time, we can remove the last index of that array. 

A list of all of the permissions and what they mean can be found in Chrome’s extension docs.

"content_scripts": [ { "matches": ["<all_urls>"], "css": ["background.css"], "js": ["background.js"] } ],

The content_scripts section sets the sites where the extension should be active. If you want a single site, like Twitter for example, you would say ["https://twitter.com/*"]. The CSS and JavaScript files are everything needed for extensions. For instance, my productive Twitter extension uses these files to override Twitter’s default appearance.

"browser_action": { "default_title": "Does a thing when you do a thing", "default_popup": "popup.html", "default_icon": { "16": "icons/icon16.png", "32": "icons/icon32.png" } }

There are things in browser_action that are also optional. For example, if the extension doesn’t need a popup for its functionality, then both the default_title and default_popup can be removed. In that case, all that’s needed the icon for the extension. If the extension only works on some sites, then Chrome will grey out the icon when it’s inactive.

Debugging

Once the manifest, CSS and JavaScript files are ready, head over to chrome://extensions/from the browser’s address bar and enable developer mode. That activates the “Load unpacked” button to add the extension files. It’s also possible to toggle whether or not the developer version of the extension is active.

I would highly recommend starting a GitHub repository to version control the files at this point. It’s a good way to save the work.

The extension needs to be reloaded from this interface when it is updated. A little refresh icon will display on the screen. Also, if the extension has any errors during development, it will show an error button with a stack trace and more info here as well.

Popup functionality

If the extension need to make use of a popup that comes off the extension icon, it’s thankfully fairly straightforward. After designating the name of the file with browser_action in the manifest file, a page can be built with whatever HTML and CSS you’ll like to include, including images (I tend to use inline SVG).

We’ll probably want to add some functionality to a popup. That make take some JavaScript, so make sure the JavaScript file is designated in the manifest file and is linked up in your popup file as well, like this: <script src="background.js"></script>

In that file, start by creating functionality and we’ll have access to the popup DOM like this:

document.addEventListener("DOMContentLoaded", () => { var button = document.getElementById("submit") button.addEventListener("click", (e) => { console.log(e) }) })

If we create a button in the popup.html file, assign it an ID called submit, and then return a console log, you might notice that nothing is actually logged in the console. That’s because we’re in a different context, meaning we’ll need to right-click on the popup and open up a different set of DevTools.

We now have access to logging and debugging! Keep in mind, though, that if anything is set in localStorage, then it will only exist in the extension’s DevTools localStorage; not the user’s browser localStorage. (This bit me the first time I tried it!)

Running scripts outside the extension

This is all fine and good, but say we want to run a script that has access to information on the current tab? Here’s a couple of ways we would do this. I would typically call a separate function from inside the DOMContentLoaded event listener:

Example 1: Activate a file function exampleFunction() { chrome.tabs.executeScript(() => { chrome.tabs.executeScript({ file: "content.js" }) }) } Example 2: Execute just a bit of code

This way is great if there’s only a small bit of code to run. However, it quickly gets tough to work with since it requires passing everything as a string or template literal.

function exampleFunction() { chrome.tabs.executeScript({ code: `console.log(‘hi there’)` }) } Example 3: Activate a file and pass a parameter

Remember, the extension and tab are operating in different contexts. That makes passing parameters between them a not-so-trivial task. What we’ll do here is nest the first two examples to pass a bit of code into the second file. I will store everything I need in a single option, but we’ll have to stringify the object for that to work properly.

function exampleFunction(options) { chrome.tabs.executeScript( { code: "var options = " + JSON.stringify(options) }, function() { chrome.tabs.executeScript({ file: "content.js" }) } ) } Icons

Even though the manifest file only defines two icons, we need two more to officially submit the extension to the Chrome Web Store: one that’s 128px square, and one that I call icon128_proper.png, which is also 128px, but has a little padding inside it between the edge of the image and the icon.

Keep in mind that whatever icon is used needs to look good both in light mode and dark mode for the browser. I usually find my icons on the Noun Project.

Submitting to the Chrome Web Store

Now we get to head over to the Chrome Web Store developer console to submit the extension! Click the “New Item” button, the drag and drop the zipped project file into the uploader.

From there, Chrome will ask a few questions about the extension, request information about the permissions requested in the extension and why they’re needed. Fair warning: requesting “activeTab” or “tabs” permissions will require a longer review to make sure the code isn’t doing anything abusive.

That’s it! This should get you all set up and on your way to building a Chrome browser extension! &#x1f389;

The post How to Build a Chrome Extension appeared first on CSS-Tricks.

Using BugHerd to Track Visual Feedback on Websites

Css Tricks - Tue, 05/19/2020 - 3:32am

BugHerd is about collecting visual feedback for websites.

If you’re like me, you’re constantly looking at your own websites and you’re constantly critiquing them. I think that’s healthy. Nothing gets better if you look at your own work and consider it perfectly finished. This is where BugHerd shines. With BugHerd, anytime you have one of those little “uh oh this area is a little rough” moments while looking at your site, you can log it to be dealt with.

Let’s take a look at a workflow like that. I’m going to assume you’ve signed up for a BugHerd account (if not grab a free trial here) and either installed the script on your site or have installed the browser extension and are using that.

I’ve done that for this very site. So now I’m looking at a page like our Archives Page, and I spot some stuff that is a little off.

I’ve taken a screenshot and circled the things that I think are visually off:

  1. The “Top Tags” and dropdown arrow are pretty far separated with nothing much connecting them. Maybe dropdowns like that should have a background or border to make that more clear.
  2. There is a weird shadow in the middle of the bottom line.

With BugHerd, I can act upon that stuff immediately. Rather than some janky workflow involving manual screenshots and opening tickets on some other unrelated website, I can do it right from the site itself.

  1. I open the BugHerd sidebar
  2. I click the green + button
  3. Select the element around where I want to give the visual feedback
  4. Enter the details of the bug

Their help video does a great job of showing this.

Here’s me logging one of those bugs I found:

Now, the BugHerd website becomes my dashboard for dealing with visual bugs. This unlocks a continual cycle of polish that that is how great websites get great!

Note the kanban board setup, which is always my prefered way to work on any project. Cards are things that need to be worked on and there are columns for cards that aren’t started, started, and finished. Perhaps your team works another way though? Maybe you have a few more columns you generally kanban with, or you name them in a specific way? That’s totally customizable in BugHerd.

I love that BugHerd itself is customizable, but at a higher level, the entire workflow is customizable and that’s even better.

  • I can set up BugHerd just for myself and use it for visual improvement work on my own projects
  • I can set up BugHerd for just the design team and we can use it among ourselves to track visual issues and get them fixed.
  • I can set up BugHerd for the entire company, so everyone feels empowered to call out visual rough spots.
  • I can set up BugHerd for clients, if I’m a freelancer or agency worker, so that the clients themselves can use it to report visual feedback.
  • I can open up BugHerd wide open so that guests of these websites can use it to report visual problems.

Check out this example of a design team with core members and guests and their preferred workflow setup:

It’s hard to imagine a better dedicated tool than BugHerd for visual feedback.

The post Using BugHerd to Track Visual Feedback on Websites appeared first on CSS-Tricks.

First Steps into a Possible CSS Masonry Layout

Css Tricks - Mon, 05/18/2020 - 10:58am

It’s not at the level of demand as, say, container queries, but being able to make “masonry” layouts in CSS has been a big ask for CSS developers for a long time. Masonry being that kind of layout where unevenly-sized elements are layed out in ragged rows. Sorta like a typical brick wall turned sideways.

The layout alone is achievable in CSS alone already, but with one big caveat: the items aren’t arranged in rows, they are arranged in columns, which is often a deal-breaker for folks.

/* People usually don't want this */ 1 4 6 8 2 7 3 5 9 /* They want this *. 1 2 3 4 5 6 7 8 9

If you want that ragged row thing and horizontal source order, you’re in JavaScript territory. Until now, that is, as Firefox rolled this out under a feature flag in Firefox Nightly, as part of CSS grid.

Mats Palmgren:

An implementation of this proposal is now available in Firefox Nightly. It is disabled by default, so you need to load about:config and set the preference layout.css.grid-template-masonry-value.enabled to true to enable it (type “masonry” in the search box on that page and it will show you that pref).

Jen Simmons has created some demos already:

CodePen Embed Fallback Is this really a grid?

A bit of pushback from Rachel Andrew

Grid isn’t Masonry, because it’s a grid with strict rows and columns. If you take another look at the layout created by Masonry, we don’t have strict rows and columns. Typically we have defined rows, but the columns act more like a flex layout, or Multicol. The key difference between the layout you get with Multicol and a Masonry layout, is that in Multicol the items are displayed by column. Typically in a Masonry layout you want them displayed row-wise.

[…]

Speaking personally, I am not a huge fan of this being part of the Grid specification. It is certainly compelling at first glance, however I feel that this is a relatively specialist layout mode and actually isn’t a grid at all. It is more akin to flex layout than grid layout.

By placing this layout method into the Grid spec I worry that we then tie ourselves to needing to support the Masonry functionality with any other additions to Grid.

None of this is final yet, and there is active CSS Working Group discussion about it.

As Jen said:

This is an experimental implementation — being discussed as a possible CSS specification. It is NOT yet official, and likely will change. Do not write blog posts saying this is definitely a thing. It’s not a thing. Not yet. It’s an experiment. A prototype. If you have thoughts, chime in at the CSSWG.

Houdini?

Last time there was chatter about native masonry, it was mingled with idea that the CSS Layout API, as part of Houdini, could do this. That is a thing, as you can see by opening this demo (repo) in Chrome Canary.

I’m not totally up to speed on whether Houdini is intended to be a thing so that ideas like this can be prototyped in the browser and ultimately moved out of Houdini, or if the ideas should just stay in Houdini, or what.

The post First Steps into a Possible CSS Masonry Layout appeared first on CSS-Tricks.

Unprefixed `appearance `

Css Tricks - Mon, 05/18/2020 - 10:56am

It’s interesting how third-parties are sometimes super involved in pushing browser things forward. One big story there was how Bloomberg hired Igalia to implement CSS grid across the browsers.

Here’s another story of Bocoup doing that, this time for the appearance property. The story is told in a Twitter thread, but the thread is broken somehow (looks like a deleted Tweet), so your best bet is to go to this one, then scroll up and down to see the whole thing. Gosh, I hope they blog it.

It took literally years of work:

2 years ago, @firefox asked us to work on a project to fix problems within the CSS appearance property. The issue came when we found out that each browser has its own implementation of how the appearance property should work on forms.

They had to do tons of research, write tests, and ultimately overhaul HTML and CSS specs. Then they needed to prove that, with those changes, browsers could un-prefix the property without breaking websites — the first attempt at this broke websites and was reverted. Then they actually get all three major browsers to do it. (Landed in Chrome, Firefox is on it, Safari has an open bug, and there is public desire to coordinate a release.)

Really goes to show just how long and grueling this work can be because it’s so crucial to get it right. If you’re into this stuff, listen to ShopTalk 407 with Brian Kardell.

The post Unprefixed `appearance ` appeared first on CSS-Tricks.

CSS fix for 100vh in mobile WebKit

Css Tricks - Fri, 05/15/2020 - 11:45am

A surprisingly common response when asking people about things they’d fix about anything in CSS, is to improve the handling of viewport units.

One thing that comes up often is how they relate to scrollbars. For example, if an element is sized to 100vw and stretches edge-to-edge, that’s fine so long as the page doesn’t have a vertical scrollbar. If it does have a vertical scrollbar, then 100vw is too wide, and the presence of that vertical scrollbar triggers a horizontal scrollbar because viewport units don’t have an elegant/optional way of handling that. So you might be hiding overflow on the body when you otherwise wouldn’t need to, for example. (Demo)

Another scenario involves mobile browsers. You might use viewport units to help you position a fixed footer along the bottom of the screen. But then browser chrome might come up (e.g. navigation, keyboard, etc), and it may cover the footer, because the mobile browser doesn’t consider anything changed about the viewport size.

Matt Smith documents this problem:

On the left, the browser navigation bar (considered browser chrome) is covering up the footer making it appear that the footer is beyond 100vh when it is not. On the right, the -webkit-fill-available property is being used rather than viewport units to fix the problem.

And a solution of sorts:

body { min-height: 100vh; min-height: -webkit-fill-available; } html { height: -webkit-fill-available; }

The above was updated to make sure the html element was being used, as we were told Chrome is updating the behavior to match Firefox’s implementation.

Does this really work? […] I’ve had no problems with any of the tests I’ve run and I’m using this method in production right now. But I did receive a number of responses to my tweet pointing to other possible problems with using this (the effects of rotating devices, Chrome not completely ignoring the property, etc.)

It would be better to get some real cross-browser solution for this someday, but I don’t see any issues using this as an improvement. It’s weird to use a vendor-prefixed property as a progressive enhancement, but hey, the world is weird.

Direct Link to ArticlePermalink

The post CSS fix for 100vh in mobile WebKit appeared first on CSS-Tricks.

Comparing Social Media Outlets for Developer Tips

Css Tricks - Fri, 05/15/2020 - 11:34am

As a little experiment, I shared a development tip on three different social networks. I also tried to post it in a format that was most suitable for that particular social network:

How did each of them “do”? Let’s take a look. But bear in mind… this ain’t scientific. This is just me having a glance at one isolated example to get a feel for things across different social media sites.

The Twitter Thread The Tweet

A little journey with lists, as a &#x1f9f5; thread.

`list-style-position: outside;` is the default for lists, and is a pretty decent default. The best part about it is that both the markers *and* the content are aligned. pic.twitter.com/CkQv1hIt6q

— CSS-Tricks (@css) April 27, 2020

Twitter is probably our largest social media outlet. Despite the fact that I’ve done absolutely nothing with it this year other than auto-tweeting posts from this site (via our Jetpack Integration), those tweets do just about as well as it ever did when I was writing each tweet. These numbers are bound to change, but at the time of writing:

Views

102,501

Followers

~446,000

Retweets

108

Engagements

3,753

Likes

428 (first tweet)

Twitter provides analytics on tweets

Going off that engagements number, a little bit less than 1% of the followers had anything to do with it. I’d say this was a very average tweet for us, if not on the low side.

The Instagram Post The Post View this post on Instagram

There are alignment things to consider with lists like an <ol>. The markers and the content. Outside positioning does well. But it uses the edge of the box as alignment and renders markers outside the box which can be bad for getting cut off. There is a solution with custom counters and subgrid though!

A post shared by Chris Coyier (@real_css_tricks) on Apr 26, 2020 at 5:44pm PDT

Instagram is by far the smallest of our social media outlets, being newer and not something I stay particularly active or consistent on. No auto-posting there just yet.

Followers

~2,800

Likes

308

Reached

2,685

Instagram provides analytics (“insights”) on posts.

Using Reach, that’s 96% of the followers. That’s pretty incredible compared t 1% of followers on Twitter. Although, on Twitter. I can easily put URLs to tweets and send people places, where my only options on Instagram are “check out the link in my profile” or use a swipe-up thing in an Instagram Story. So, despite the high engagement of Instagram, I’m mostly just getting the satisfaction of teaching something as well as a little brand awareness. It’s much harder for me to get you to directly do something from Instagram.

The YouTube Video The Video

YouTube is in the middle for us, much bigger than Instagram but not as big as Twitter. YouTube is a little unique in that there can be (and are) advertising directly on the videos and that get’s a “revenue share” from YouTube. That’s very much not driving motivation for using YouTube (I make 50 cents a day, but it is unique compared to the others.

Subscribers

51,300

Likes

116

Views

2,455

YouTube provides video analytics Facebook?

We do have a Facebook page but it’s the most neglected of all of them. We auto-post new articles to it, but this experiment didn’t really have a blog post. I published the video to our site, but that doesn’t get auto-posted to Facebook, so the tip never made it there.

I used to feel a little guilty about not taking as much advantage of Facebook as I could, but whenever I look at overall analytics, I’m reminded that all of social media accounts combine for ~2% of traffic to this site. Spending any more time on this stuff is foolish for me, when that time could be spent on content for this site and information architecture for what we already have. And for Facebook specifically, whatever time we have spent there has never seemed to pan out. Just not a hive for developers.

CodePen?

I probably should have factored CodePen into this more, since it’s something of a social network itself with similar metrics. I worked on the examples in CodePen and the whole video was done in CodePen. But in this case, it was more about the journey than the destination. I did ultimately link to a demo at the end of the Twitter thread, but Instagram can’t link to it and I wasn’t as compelled to link to it on YouTube as the video itself to me was the important information.

If I was trying to compare CodePen stats here, I would have created the Pen in a step-by-step educational format so I could deliver the same idea. That actually sounds fun and I should probably still do that!

Winner?

Eh.

The problem is that there isn’t anything particularly useful to measure. What would have been way more interesting is if I had some really important call to action in each one where I’m like trying to sell you something or get you to sign up for something or whatever. I feel like that’s the real world of developer marketing. You gotta do 100 things for someone for free if you want them to do something for you on that 101st time. And on the 101st time, you should probably measure it somehow to see if the effort is worth it.

Here’s the very basic data together though…

FollowersEngagements%Twitter~446,0003,7530.08%Instagram~2,8002,68596%YouTube~51,3002,4555%

One interesting thing is that I find the effort was about equal for all of them. You’d think a video would be hardest, but at least that’s just hit-record-hit-stop and minor editing. The other formats take longer to craft with custom text and graphics.

These would be my takeaways from this limited experiment:

  • You need big numbers on Twitter to do much. That’s because the engagement is pretty low. Still, it’s probably our best outlet for getting people to click a link and do something.
  • Instagram has amazing engagement, but it’s hard to send anyone anywhere. It’s still no wonder why people use it. You really do reach your audience there. If you had a strong call to action, I bet you could still get people do to it even with the absence of links (since people know how to search for stuff on the web).
  • While I mentioned that for this example the effort level was fairly even, in general, YouTube is going to require much higher effort. Video production just isn’t the same as farting out a couple of words or a screenshot. With that, and knowing that you’d need absolutely massive numbers to earn anything directly from YouTube, it’s pretty similar to other social networks in that you need to derive value from it abstractly.
  • This was not an idea that “went viral” in any sense. This is just standard-grade engagement, which was good for this experiment. I’m always super surprised at the type of developer tips that go viral. It’s always something I don’t expect, and often something I’m like awwwww we have an article about that too! I’d never bet on or expect anything going viral. Making stuff that your normal audience likes is the ticket.
  • Being active is pretty important. Any chart I’ve seen has big peaks when posts go out regularly and valleys when they don’t. Post regularly = riding the peaks.
  • None of this compares anywhere close to the real jewel of making things: blogging. Blogging is where you have full control and full benefit. The most important thing social media can do is get people over to your own site.

The post Comparing Social Media Outlets for Developer Tips appeared first on CSS-Tricks.

WTF is a Static API

Css Tricks - Fri, 05/15/2020 - 4:40am

Just like there is a movement to make more websites (and more of websites) from pre-rendered static files (Jamstack), so to might we consider moving content-based APIs to be static. Sean C Davis:

static API is simply a collection of flat JSON files that live on a content delivery network (CDN). It doesn’t perform any action other than delivering content (static JSON files) to the requesting user.

But that doesn’t mean a static API is simple. And every file doesn’t have to be manually generated or updated. A static API can still use a database and it can still pull data from external services. In other words, it can be dynamically generated, but it is statically delivered.

The use cases are probably somewhat limited, but I still like it. In a sense, all RSS feeds (or, more directly, JSON feeds) could be done this way, and many are.

Direct Link to ArticlePermalink

The post WTF is a Static API appeared first on CSS-Tricks.

Notion-Powered Websites

Css Tricks - Thu, 05/14/2020 - 10:10am

I’m a big fan of Notion, as you likely know from previous coverage and recent video. It’s always interesting to see what other people do with Notion, and even how Notion uses Notion.

I’d say most usage of Notion is private and internal, but any page on Notion can be totally public with the flip of a switch. We do that with some stuff like our post ideas page and here’s a simple camping checklist I made for myself.

That’s pretty rad. You could use that for lots of business-things you might otherwise literally build a website to do. Maybe a public product roadmap, a job posting, a press release an announcement…

But it’s not quite a website. You don’t get a custom domain name. You don’t have any analytics on it. You’re limited by the exact feature set of Notion.

People have been trying to extract the data out of Notion and essentially use it as a CMS for a while now…

But all those ways are, ya know, a decent amount of effort.

Stephen Ou recently showed me a pretty cool idea he has called Fruition. It’s entirely free, and also a bit of effort to set up, but once you’re done, you have your own domain name that hosts a Notion page and gives you some control over it (like putting in fonts and scripts and such).

It’s very clever in that it uses Cloudflare Workers to do all the heavy lifting.

This is probably the easiest-to-manage website ever. Just open Notion, change stuff, done.

Stephen admits Fruition is somewhat complex to set up. If you’re looking for something easier and perhaps more flexible, check out Super.

Actual neumorphism in the wild!

I would note that none of these things are official Notion products or affiliates of it in any way. Honestly, they all make me a little nervous in that they could break if Notion ever decides they don’t care for the product to be used this way. I also feel like Notion has been saying an API is something they’d like to offer for a while. That will be the real answer to all this and there will be a proliferation of third-party products once we have it.

The post Notion-Powered Websites appeared first on CSS-Tricks.

Online Together

Css Tricks - Thu, 05/14/2020 - 4:16am

(This is a sponsored post.)

An Event Apart: Online Together is a single-day online conference with an intense focus on digital design, UX, content, code, and more, giving you deep insights into where we are now and where things are going next.

AEA! With a brand new online version of their conference! That’s awesome. AEA is a best-in-class web conference, so if you’re looking for a conference to help get your mind going on big-picture web design and development stuff again and connect with some like-minded industry people, this is your opportunity.

Direct Link to ArticlePermalink

The post Online Together appeared first on CSS-Tricks.

Equal Width Columns in CSS Grid are Kinda Weird

Css Tricks - Wed, 05/13/2020 - 1:13pm

Everything is flexible these days. If you write grid-template-columns: 200px 200px 200px;, sure, you’d have equal-width columns, but that’s a rare day. What you usually mean is three columns of equal fluid width.

We’ve got fractional units for that, like grid-template-columns: 1fr 1fr fr;. That’s usually fine, but they aren’t very sturdy like pixels. A large bit of media (or something like a <pre>, or long bit of text like a URL) can cause those columns to stretch and that’s almost never what you want. I’ve called that a grid blowout. The big idea is that the minimum width of a 1fr column is auto, not 0. In other words,. those widened columns are just being as narrow as they know how to be!

To fix that, we can do like:

.el { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }

..or we could shorten it:

.el { grid-template-columns: repeat(3, minmax(0, 1fr)); }

It’s a little awkward, but OK. You only gotta learn it once. You might not even ever run into this if you’re always setting max-width on your media and handling line breaks.

If you want to make your columns sturdy again without the minmax dance, you could use percentages rather than pixels and stay flexible. But what percentage do you use? 33.33%? That’s fine as long as you don’t have any gap — otherwise, the gap will add to the width and overflow the container. You could fake gaps by putting padding inside the columns, but that’s a little janky and uneven.

This whole thing comes from a great tweet from Wes Bos:

&#x1f525; A visual guide to getting equal width columns in CSS Grid pic.twitter.com/PY4yYokY18

— Wes Bos (@wesbos) May 1, 2020

I know a ton of people run into this — based on the number of emails I get about the grid blowout article — so it’s worth kinda internalizing why all this is the way it is. It probably should be easier but I don’t have any particular suggestions on how it could be.

The post Equal Width Columns in CSS Grid are Kinda Weird appeared first on CSS-Tricks.

Pseudo-elements in the Web Animations API

Css Tricks - Wed, 05/13/2020 - 1:13pm

To use the Web Animations API (e.g. el.animate()) you need a reference to a DOM element to target. So, how do you use it on pseudo-elements, which don’t really offer a direct reference? Dan Wilson covers a (newish?) part of the API itself:

const logo = document.getElementById('logo'); logo.animate({ opacity: [0, 1] }, { duration: 100, pseudoElement: '::after' });

I noticed in Dan’s article that ::marker is supported. I was just playing with that recently while doing our List Style Recipes page. I figured I’d give it a spin by testing the WAAPI and @keyframes on both a ::marker and and ::after element:

CodePen Embed Fallback

At first, I confused myself because it seemed like the WAAPI wasn’t working on ::after, but Dan reminded me that when using a transform, the element can’t be display: inline. Instead, I made it inline-block and it worked fine. However, I did uncover that @keyframes don’t seem to work on ::marker elements in Firefox — hopefully they’ll fix that (and we get Chrome and Safari support for ::marker ASAP).

Direct Link to ArticlePermalink

The post Pseudo-elements in the Web Animations API appeared first on CSS-Tricks.

React Single File Components Are Here

Css Tricks - Tue, 05/12/2020 - 3:56pm

Shawn Wang is talking about RedwoodJS here:

…  it is the first time React components are being expressed in a single file format with explicit conventions.

Which is the RedwoodJS idea of Cells. To me, it feels like a slightly cleaner version of how Apollo wants you to do it with useQuery. Shawn makes that same connection and I know RedwoodJS uses Apollo, so I’m thinking it’s some nice semantic sugar.

There is a lot of cool stuff going on in RedwoodJS. “A highly opinionated stack” if its helpful to think of it that way, but Tom made clear in our last episode of ShopTalk that it’s not like Rails. Not that Rails is bad (it isn’t), but that this new world can do things in new and better ways that make for long-term healthy software.

The post React Single File Components Are Here appeared first on CSS-Tricks.

Using CSS Masks to Create Jagged Edges

Css Tricks - Tue, 05/12/2020 - 10:31am

I was working on a project that had this neat jagged edge along the bottom of a banner image.

Looking sharp… in more ways than one.

It’s something that made me think for a second and I learned something in the process! I thought I’d write up how I approached it so you can use it on your own projects.

I started out with a good old fashioned HTML image in a wrapper element:

<div class="jagged-wrapper">   <img src="path-to-image.jpg" /> </div>

Then I used its ::after pseudo element to drop a repeating background image on it:

.jagged-wrapper::after {   content: "";   background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none"><polygon style="fill:white;" points="1,0 1,1 0,1 "/></svg>');   background-size: 30px 30px;   width: 100%;   height: 30px;   position: absolute;   bottom: 0px;   right: 0;   z-index: 2; }

That background image? It’s SVG code converted to a Data URI. Here’s the original SVG code. Chris has a nice video where he walks through that conversion.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none">   <polygon style="fill: white;" points="1,0 1,1 0,1 "/> </svg>

“There we go!” I thought.

CodePen Embed Fallback

While that certainly works, geez, that’s a lot of hassle. It’s difficult to read SVG markup in CSS like that. Plus, it’s annoying to have to remember to quote them (e.g. url('data:image/svg+xml'...)). Sure, we can base64 encode the SVG to avoid that, but that’s even more annoying. Plus, the SVG needs to be filled with the same background color as the image (or wherever it is used), or else it won’t work.

Wait, isn’t this what masking is for? Yes! Yes, this is what masking is for.

That led me to a new approach: use an image like the one above as a CSS mask so that the “missing” bits of the banner image would actually be missing. Rather than drawing triangles of the background color on top of the banner, we should instead mask away those triangles from the banner entirely and let the real background show through. That way, it works on any background!

Masking is pretty much supported everywhere — at least in the simple way I’m talking about here. We’re also talking about something that can be implemented with progressive enhancement; if masks aren’t supported in a given browser, then you just don’t get the sawtooth effect. Definitely not the end of the world.

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

DesktopChromeFirefoxIEEdgeSafari85*53No81*TP*Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari81*6881*13.4*

One way a CSS mask works is to provide an image with an alpha channel as a mask-image. The underlying element — the one that’s being masked — becomes (semi-)transparent to the degree that the alpha channel of the mask-image dictates. So if your mask image is a white teapot on a transparent background, then the masked element will be cut to the shape of the teapot and everything outside that will be hidden.

CodePen Embed Fallback

Masking can be a tricky concept to grok. Sarah Drasner has an article that takes a deep-dive on masking, including how it is different from clipping. There’s much more that masks can do than what we’re covering here. Check the specs, caniuse, and MDN for even more information.

What we need is a single “sawtooth”-like image similar to the SVG above, where the top-left half is filled white and the bottom-left is left semi-transparent. And, ideally that image wouldn’t be an actual SVG, since that would land us back into the ugly data URI mess we were in before.

At this point you might be thinking: “Hey, just embed the SVG in the CSS directly, define a mask in it, then point the CSS at the mask ID in the SVG!”

Nice idea! And it’s certainly doable, if you can edit the HTML. For my specific project, however, I was working in WordPress and I really wanted to confine my changes to pure CSS rather than injecting extra parts into the HTML. That would have been a lot more work. I don’t think this is uncommon; for a presentational change like this, not having to edit the HTML is useful. We’re mostly on board with the idea of avoiding semantically worthless wrapper elements just to provide styling hooks, but I feel that also applies to adding entire SVG markup to the document… or even a WordPress template.

We can use a CSS linear gradient to create a triangle shape instead:

.el {   linear-gradient(     to bottom right,     white,     white 50%,     transparent 50%,     transparent   ); }

Here that is on a radial background, so you can see it’s really transparent:

CodePen Embed Fallback

Great! We can just use that as a mask-image on our banner, right? We need to set mask-size, which is like background-size, and mask-repeat, like background-repeat, and we’re good?

CodePen Embed Fallback

Unfortunately, no. Not so good.

The first reason is that, unless you’re using Firefox, you’ll likely see no masking at all on that example above. This is because Blink and WebKit still only support masking with a vendor prefix at the time of writing. That means we need -webkit- prefixed versions of everything.

CodePen Embed Fallback

Vendor prefixing aside, what we’re doing is also conceptually wrong. If we confine the mask to just the bottom stripe of the image with mask-size, then the rest of the image has no mask-image at all, which masks it out entirely. As a result, we can’t use the sawtooth alone as a mask. What we need is a mask-image that is a rectangle the size of the image with just a sawtooth at the bottom. 

Something like this:

CodePen Embed Fallback

We do that with two gradient images. The first image is the same sawtooth triangle as above, which is set to repeat-x and positioned at the bottom so that it repeats only along the bottom edge of the image. The second image is another gradient that is transparent for the bottom 30px (so as to not interfere with the sawtooth), opaque above that (which is shown going from black to white in the demo), and takes up the whole size of the element. 

So we now have this wedge-shaped piece, with a single triangle sawtooth at the bottom, and it occupies the entire height of our banner image in two separate pieces. Finally, we can use these pieces with mask-image by repeating them horizontally across our image, and it should have the effect we want:

CodePen Embed Fallback

And there we have it!

The post Using CSS Masks to Create Jagged Edges appeared first on CSS-Tricks.

Why does writing matter in remote work?

Css Tricks - Tue, 05/12/2020 - 7:24am

Talk to anyone who has an active blog and I bet they’ll tell you it’s been valuable to them. Maybe it’s opened doors. Maybe it’s got them a job. Maybe it’s got them a conference invite. Maybe they just like the thrill of knowing people have read and responded to it. Maybe they learned a lot through its creation and maintenance.

Khoi Vinh said:

It’s hard to overstate how important my blog has been, but if I were to try to distill it down into one word, it would be: “amplifier.”

But what about other kinds of writing? Just day to day writing? Is that important for web workers? “Especially now”?

Tim Casasola:

In remote work, we communicate primarily through writing. We send messages in Slack. We document projects in Notion. We send meeting invites with a written description of the purpose. We’re writing all the time.

It’s just so damn important for team work of any kind, particularly when you aren’t next to each other physically.

While writing forces people to think clearly, writing also forces teams to think clearly. In my experience, having a clearly written thing makes it easy for folks to collaborate with me. This is because people naturally enjoy poking holes in arguments, adding points that were missed, or mentioning any risks that weren’t taken into account. I’ve found it helpful to use this human tendency to my advantage. Extra opinions and poked holes are hard to surface if you didn’t write something in the first place.

Direct Link to ArticlePermalink

The post Why does writing matter in remote work? appeared first on CSS-Tricks.

Accepting Payments (including Recurring Payments) on WordPress.com

Css Tricks - Tue, 05/12/2020 - 6:49am

I’m a fan of building websites with the least amount of technical debt and things you have to be responsible for as possible for what you wanna do. Sometimes you take on this debt on purpose because you have to, but when you don’t, please don’t ;).

Let’s say you need to build a site that can take money from customers, but on a recurring basis. WordPress.com can do that now, and it’s a fantastic choice because it’s all of the power and control and none of the debt.

Here’s my thinking…

1) WordPress.com is the fastest way to spin up a WordPress site.

Not only is it fast, but you don’t have to worry about anything. Servers, SSL, security, performance, accessibility… that’s all handled for you and you can focus on what you do best. Even if you’re a seasoned developer, I’m sure you can understand how this is compelling. Automating work is what the best developers do.

2) WordPress.com sites can be eCommerce sites.

Not only sell-a-product style sites, but also recurring payments sites. Meaning you can very easily set up a subscription service, membership site, or site for monthly donations.

The pricing is like this:

WordPress.com PlanJetpack planRelated FeesWordPress.com eCommerce —NoneWordPress.com BusinessJetpack Professional2%WordPress.com PremiumJetpack Premium4%WordPress.com PersonalJetpack Personal 8%

So you do the math and figure out the most economical plan for you. That eCommerce plan on WordPress.com is only $45/month and means zero additional fees, so I imagine once you’re up and running and making sales, that plan becomes the obvious choice.

Ideas!
  • You build custom weekly meal plans for families and charge monthly for that.
  • You have a membership site for physical training videos where people have to be a member to see the videos.
  • Your site is has a bunch of completely free great content, and you offer a way to give yearly donations to support it.

Why roll your own eCommerce when you don’t have to?

3) It used to be that your WordPress site was a bit limited on WordPress.com, but those days are over.

eCommerce is one aspect of that, but I’m talking full SFTP and database access. You can build custom themes, use your own plugins, just like any other WordPress site. So if you’re thinking that you’re giving up too much control by going with WordPress.com, make sure to re-evaluate that.

So knowing all that, I’d say you really should give WordPress.com a hard look when you’re about to spin up an eCommerce site. I’ve seen far too much over-engineering and mountains of technical debt in my life, I’d rather see people use simpler tools and get started doing their actual business, especially to start.

The post Accepting Payments (including Recurring Payments) on WordPress.com appeared first on CSS-Tricks.

Dealing With Stale Props and States in React’s Functional Components

Css Tricks - Tue, 05/12/2020 - 4:56am

There’s one aspect of JavaScript that always has me pulling my hair: closures. I work with React a lot, and the overlap there is that they can sometimes be the cause of stale props and state. We’ll get into exactly what that means, but the trouble is that the data we use to build our UI can be totally wrong in unexpected ways, which is, you know, bad.

Stale props and states

Long story short: it’s when code that is executed asynchronously has a reference to a prop or state that is no longer fresh, and thus, the value it returns is not the latest one.

To be even more clear, let’s play around with the same stale reference example React has in its documentation.

function Counter() {   const [count, setCount] = useState(0);   function handleAlertClick() {     setTimeout(() => {       alert("You clicked on: " + count);     }, 3000);   }   return (     <div>       <p>You clicked {count} times</p>       <button onClick={() => setCount(count + 1)}>Click me</button>       <button onClick={handleAlertClick}>Show alert</button>     </div>   ); }

(Live demo)

Nothing fancy here. We have a functional component named Counter. It keeps track of how many times the user has clicked one button and shows an alert that displays how many times that button was clicked when clicking another button. Try this:

  1. Click the “Click me” button. You are going to see the click counter go up.
  2. Now click the “Show alert”button. Three seconds should go by and then trigger an alert telling you how many times you clicked the “Click me” button.
  3. Now, click the “Show alert” button again and quickly click the “Click me” button before it triggers the alert in three seconds.

See what happens? The count shown on the page and the count shown in the alert do not match. The number in the alert is not just some random number, though. That number is the value the count variable had in the moment the asynchronous function inside the setTimeout was defined, which is the moment the “Show alert” button is clicked.

That’s just how closures work. We’re not going to get into the specifics of them in this post, but here are some docs that cover them in greater detail.

Let’s focus on how we can avoid these stale references with our states and props.

React offers a tip on how to deal with stale dates and props in the same documentation where the example was pulled.

If you intentionally want to read the latest state from some asynchronous callback, you could keep it in a ref, mutate it, and read from it.

By keeping the value  asynchronously in a ref, we can bypass stale references. If you need to know more about ref in functional components, React’s documentation has a lot more information.

So, that begs the question: How can we keep our props or state in a ref?

Let’s do it the dirty way first.

The dirty way to store props and state in a ref

We can easily create a ref using useRef() and use count as its initial value. Then, wherever the state is being updated, we set the ref.current property to the new value. Lastly, use ref.current instead of count in the asynchronous part of our code.

function Counter() {   const [count, setCount] = useState(0);   const ref = useRef(count); // Make a ref and give it the count   function handleAlertClick() {     setTimeout(() => {       alert("You clicked on: " + ref.current); // Use ref instead of count     }, 3000);   }   return (     <div>       <p>You clicked {count} times</p>       <button         onClick={() => {           setCount(count + 1);           ref.current = count + 1; // Update ref whenever the count changes         }}       >         Click me       </button>       <button         onClick={() => {           handleAlertClick();         }}       >         Show alert       </button>     </div>   ); }

(Live demo)

Go ahead and do the same as last time. Click “Show alert” and then click “Click me” before the alert is triggered in three seconds.

Now we have the latest value!

Here’s why it works. When the asynchronous callback function is defined inside setTimeout, it saves a reference to the variables it uses, which is count in this case. This way, when the state updates, React not only changes the value but the variable reference in memory is completely different as well.

This means that — even if the state’s value is non-primitive — the variable you are working with in your asynchronous callback is not the same in memory. An object that would typically keep its reference throughout different functions now has a different value.

How does using a ref solve this? If we take a quick look at React’s docs again, we find an interesting, but easy-to-miss, bit of information:

[…] useRef will give you the same ref object on every render.

It doesn’t matter what we do. Throughout the lifetime of your component, React will give us the exact same ref object in memory. Any callback, no matter when it’s defined or executed, is working with the same object. No more stale reference.

The cleaner way to store props and state in a ref

Let’s be honest… using a ref like that is an ugly fix. What happens if your state is being updated in a thousand different places? Now you have to change your code and manually update the ref in all those places. That’s a no-no.

We are going to make this more scalable by giving ref the value of the state automatically when the state changes.

Let’s start by getting rid of the manual change to the ref in the “Click me”button.

Next, we make a function called updateState that is called whenever we need to change the state. This function takes the new state as an argument and it sets the ref.current property to the new state and updates the state as well with that same value.

Finally, let’s substitute the original setCount function React gives us with the new updateState function where the state is being updated.

function Counter() {   const [count, setCount] = useState(0);   const ref = useRef(count);   // Keeps the state and ref equal   function updateState(newState) {     ref.current = newState;     setCount(newState);   }   function handleAlertClick() { ... }   return (     <div>       <p>You clicked {count} times</p>       <button         onClick={() => {           // Use the created function instead of the manual update           updateState(count + 1);         }}       >         Click me       </button>       <button onClick={handleAlertClick}>Show alert</button>     </div>   ); }

(Live demo)

Using a custom hook

The cleaner solution works just fine. It gets the job done just like the dirty solution, but only calls a single function to update the state and ref.

But guess what? We can do better. What if we need to add more states? What if we want to do this in other components too? Let’s take the state, ref and updateState function and make them truly portable. Custom hooks to the rescue!

Outside the Counter component, we are going to define a new function. Let’s name it useAsyncReference. (It can be named anything, really, but note that it’s common practice to name custom hooks with “use” as a prefix.) Our new hook will have a single parameter for now. We’ll call it value.

Our previous solution had the same information stored twice: once in the state and once in the ref. We are going to optimize that by keeping the value just in ref this time. In other words, we will create a ref and give it the value parameter as its initial value.

Right after the ref, we will make an updateState function that takes the new state and sets it to the ref.current property.

Lastly, we return an array with ref and the updateState function, very similar to what React does with useState.

function useAsyncReference(value) { const ref = useRef(value); function updateState(newState) { ref.current = newState; } return [ref, updateState]; } function Counter() { ... }

We are forgetting something! If we check the useRef documentation, we learn that updating a ref does not trigger a re-render. So, while ref has the updated value, we wouldn’t see the changes on screen. We need to force a re-render every time ref gets updated.

What we need is a fake state. The value doesn’t matter. It’s only going to be there to provoke the re-render. We can even ignore the state and only keep its update function. We are calling that update function forceRender and giving it an initial value of false.

Now, inside updateState, we force the re-render by calling forceRender and passing it a state different to the current one after setting ref.current to newState.

function useAsyncReference(value) {   const ref = useRef(value);   const [, forceRender] = useState(false);   function updateState(newState) {     ref.current = newState;     forceRender(s => !s);   }   return [ref, updateState]; } function Counter() { ... }

Take whatever value it has and return the opposite. The state doesn’t really matter. We are merely changing it so React detects a change in state and re-renders the component.

Next, we can clean the Count component and remove the previously used useState, ref and updateState function, then implement the new hook. The first value of the returned array is the state in the form of a ref. We’ll keep calling it count, where the second value is the function to update the state/ref. We’ll continue calling it setCount.

We also have to change the references to the count since now that they all must be count.current. And we must call setCount instead of calling updateState.

function useAsyncReference(value) { ... } function Counter() {   const [count, setCount] = useAsyncReference(0);   function handleAlertClick() {     setTimeout(() => {       alert("You clicked on: " + count.current);     }, 3000);   }   return (     <div>       <p>You clicked {count.current} times</p>       <button         onClick={() => {           setCount(count.current + 1);         }}       >         Click me       </button>       <button onClick={handleAlertClick}>Show alert</button>     </div>   ); } Making this work with props

We have a truly portable solution for our problem. But guess what… there’s still a little more to do. Specifically, we need to make the solution compatible with props.

Let’s take the “Show alert” button and handleAlertClick function to a new component outside the Counter component. We are gonna call it Alert and it’s going to take a single prop called count. This new component is going to show the count prop value we are passing it in an alert after a three second delay.

function useAsyncReference(value) { ... } function Alert({ count }) {   function handleAlertClick() {     setTimeout(() => {       alert("You clicked on: " + count);     }, 3000);   }   return <button onClick={handleAlertClick}>Show alert</button>; } function Counter() { ... }

In Counter, we’re swapping the “Show alert” button for the Alert component. We’ll pass count.current to the count prop.

function useAsyncReference(value) { ... } function Alert({ count }) { ... } function Counter() {   const [count, setCount] = useAsyncReference(0);   return (     <div>       <p>You clicked {count.current} times</p>       <button         onClick={() => {           setCount(count.current + 1);         }}       >         Click me       </button>       <Alert count={count.current} />     </div>   ); }

(Live demo)

Alright, time to run through the testing steps again. See? Even though we are using a safe reference to the count in Counter, the reference to the count prop in the Alert component is not asynchronously safe and our custom hook is not suitable to use with props… yet.

Lucky for us, the solution is fairly simple.

All we have to do is add a second parameter to our useAsyncReference hook named isProp, with false as the initial value. Just before we return the array with ref and updateState, we set up a condition. If isProp is true, we set the ref.current property to value and only return ref.

function useAsyncReference(value, isProp = false) {   const ref = useRef(value);   const [, forceRender] = useState(false);   function updateState(newState) {     ref.current = newState;     forceRender(s => !s);   }   if (isProp) {     ref.current = value;     return ref;   }   return [ref, updateState]; } function Alert({ count }) { ... } function Counter() { ... }

Now let’s update Alert so that is uses the hook. Remember to pass true as a second argument to useAsyncReference since we are passing a prop and not a state.

function useAsyncReference(value) { ... } function Alert({ count }) {   const asyncCount = useAsyncReference(count, true);   function handleAlertClick() {     setTimeout(() => {       alert("You clicked on: " + asyncCount.current);     }, 3000);   }   return <button onClick={handleAlertClick}>Show alert</button>; } function Counter() { ... }

(Live demo)

Give it another try. Now it works perfectly whether you use states or props.

One last thing…

There’s one last change I’d like to make. React’s useState docs tell us that React will bail out of a re-render if the new state is identical to the previous one. Our solution doesn’t do that. If we pass the current state again to the hook’s updateState function, we will force a re-render no matter what. Let’s change that.

Let’s put the body of updateState inside an if statement and execute it when ref.current is different than the new state. The comparison must be done with Object.is(), just like React does.

function useAsyncReference(value, isProp = false) {   const ref = useRef(value);   const [, forceRender] = useState(false);   function updateState(newState) {     if (!Object.is(ref.current, newState)) {       ref.current = newState;       forceRender(s => !s);     }   }   if (isProp) {     ref.current = value;     return ref;   }   return [ref, updateState]; } function Alert({ count }) { ... } function Counter() { ... }

Now we are finally done!

React can sometimes seem like a black box that is full of little quirks. Those quirks might be daunting to deal with, like the one we just tackled. But if you are patient and enjoy being challenged, you’ll soon realize it’s an awesome framework and a pleasure to work with.

The post Dealing With Stale Props and States in React’s Functional Components appeared first on CSS-Tricks.

How I Put the Scroll Percentage in the Browser Title Bar

Css Tricks - Tue, 05/12/2020 - 4:54am

Some nice trickery from Knut Melvær.

Ultimately the trick boils down to figuring out how far you’ve scrolled on the page and changing the title to show it, like:

document.title = `${percent}% ${post.title}`

Knut’s trick assumes React and installing an additional library. I’m sure that library does all kinds of smart stuff, but if you’re looking to do this “vanilla” style, I’d probably rock something like this…

const percentLabel = document.querySelector("#percent"); const originalTitle = document.title; window.addEventListener("scroll", () => { let scrollTop = window.scrollY; let docHeight = document.body.offsetHeight; let winHeight = window.innerHeight; let scrollPercent = scrollTop / (docHeight - winHeight); let scrollPercentRounded = Math.round(scrollPercent * 100); percentLabel.innerHTML = scrollPercentRounded; document.title = `(${scrollPercentRounded}%) ${originalTitle}`; });

Here’s a project, and here’s the deployed site so you can see it in action.

Direct Link to ArticlePermalink

The post How I Put the Scroll Percentage in the Browser Title Bar appeared first on CSS-Tricks.

CSS Animation Timelines: Building a Rube Goldberg Machine

Css Tricks - Mon, 05/11/2020 - 1:34pm

If you’re going to build a multi-step CSS animation or transition, you have a particular conundrum. The second step needs a delay that is equal to the duration of the first step. And the third step is equal to the duration of the first two steps, plus any delay in between. It gets more and more complicated until you might just be like, nahhhhh I’ll use more technology to help me.

Paul Hebert:

Lately I’ve been using custom properties to plan out pure CSS timelines for complex animations.

Cool. And it can get completely nuts.

Direct Link to ArticlePermalink

The post CSS Animation Timelines: Building a Rube Goldberg Machine appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.