Tag: just

Just How Long Should Alt Text Be?

I teach a class over at the local college here in Long Beach and a majority of the content is hosted on the Canvas LMS so students can access it online. And, naturally, I want the content to be as accessible as possible, so thank goodness Canvas has a11y tooling built right into it.

But it ain’t all that rosy. It makes assumptions like all other a11y tooling and adheres to guidelines that were programmed into it. It’s not like the WCAG is baked right in and updated when it updates.

The reason this is even on my mind is that Jeremy yesterday described his love for writing image descriptions:

I enjoy writing alt text. I recently described how I updated my posting interface here on my own site to put a textarea for alt text front and centre for my notes with photos. Since then I’ve been enjoying the creative challenge of writing useful—but also evocative—alt text.

I buy into that! Writing alt text is a challenge that requires a delicate dance between the technical and the creative. It’s both an opportunity to make content more accessible and enhance the user experience.

One of those programmed guidelines in the Canvas tool is a cap of 120 characters on alt text. Why 120? I dunno, I couldn’t find any supporting guideline or rule for that exact number. One answer is that screen readers stop announcing text after 125 characters, but that’s apparently untrue, at least today. The general advice for how long alt text should be comes in varying degrees:

  • Jake Archibald talks of length in terms of emotion. Detail is great, but too much detail might distort the focal point, which makes total sense.
  • Dave sees them as short, succinct paragraphs.
  • Carrie Fisher suggests a 150-character limit not because screen readers will truncate them but more as a mental note that maybe things are getting too descriptive.
  • Daniel Göransson says in this 2017 guide that it comes down to context and knowing when certain details of an image are worth additional explanation. But he generally errs on the side of conciseness.

So, how long should alt text be? The general consensus here is that there is no hard limit, but more of a contextual awareness of what purpose the image serves and adapting to it accordingly.

Which gets me back to Jeremy’s article. He was writing alt text for a group of speaker headshots and realized the text was all starting to sound the same. He paused, thought about the experience, compared it to the experience of a sighted user, and created parity between them:

The more speakers were added to the line-up, the more I felt like I was repeating myself with the alt text. […] The experience of a sighted person looking at a page full of speakers is that after a while the images kind of blend together. So if the alt text also starts to sound a bit repetitive after a while, maybe that’s not such a bad thing. A screen reader user would be getting an equivalent experience.

I dig that. So if you’re looking for a hard and fast rule on character counts, sorry to disappoint. Like so many other things, context is king and that’s the sort of thing that can’t be codified, or even automated for that matter.

And while we’re on the topic, just noticed that Twitter has UI to display alt text:

Now if only there was more contrast between that text and the background… a11y is hard.

Just How Long Should Alt Text Be? originally published on CSS-Tricks. You should get the newsletter.

CSS-Tricks

, , ,

A New Container Query Polyfill That Just Works

There is now a polyfill for Container Queries that behaves as perfectly as a polyfill should:

  1. You conditionally load it when you detect the browser doesn’t support Container Queries.
  2. You write CSS as you normally would, including current-spec-compliant Container Queries syntax code.
  3. It just works.

It’s pretty great to have a container query polyfill that is this easy to use and from Chrome itself, the first-movers as far as early test implementations. Looks like Surma put it together — thanks Surma!

There was a Container Query polyfill from Jonathan Neal called cqfill that predates this. I’m not sure if it’s officially deprecated, but it required extra non-spec CSS to work and PostCSS processing, so I’d consider it deprecated in favor of this newer polyfill.

Loading the polyfill is like this:

// Support Test const supportsContainerQueries = "container" in document.documentElement.style;  // Conditional Import if (!supportsContainerQueries) {   import("https://cdn.skypack.dev/container-query-polyfill"); }

You can pull it from npm or use as a <script>, but this way seems best to me to keep things light and easy.

Then you’re free to use the syntax for a container query in CSS. Say you have a weather widget in HTML. You’ll need an extra wrapper element for your queries. That’s just the rule: you can’t query the thing you style.

<div class="weather-wrap">   <dl class="weather">     <div>       <dt>Sunday</dt>       <dd>         <b>26°</b> 7°       </dd>     </div>     <div>       <dt>Monday</dt>       <dd>         <b>34°</b> 11°       </dd>     </div>     <!-- etc -->   </dl> </div>

The wrapper is instantiated as a container:

.weather-wrap {   container: inline-size / weather-wrapper;   /* Shorthand for: */   /* container-type: inline-size; */   /* container-name: weather-wrapper; */    /* For quick testing, do this to get a resize handle on desktop: */   /* resize: both; */   /* overflow: hidden; */ }

Then you write any global styling for that component, as well as container query scoped styles:

.weather {   display: flex; } @container weather-wrapper size(max-width: 700px) {   .weather {     flex-direction: column;   } }

Container Queries polyfill example

Here’s that slightly more fleshed-out demo of the Container Query polyfill using an actual weather widget:

I first saw this over on Bramus’ blog, and he’s got a classic card demo going with this Container Query polyfill. Scroll up and down. You’ll see a row of bear cards at the top (if your browser window is wide enough), and then similar bear cards in different layout positions below that change into nicer formats when they can, based on the container query.

Container Query polyfill browser support

The polyfill docs say:

The polyfill relies on ResizeObserverMutationObserver and :is(). Therefore, it should work in all modern browsers, specifically Chrome/Edge 88+, Firefox 78+ and Safari 14+.

There are all sorts of other minor little caveats covered in those docs, including what it does and doesn’t support. Seems like mostly niche stuff to me — the main/typical use cases are covered.

A game changer?

As I write, we’ve seen behind-flag support for Container Queries in Chrome, and it is an official spec draft now:

That’s extremely exciting and points heavily toward browsers actually shipping with Container Queries, even if the syntax changes a bit on the way (it already has a number of times). But, of course, we have no idea if/when Container Queries do ship — and when that magical threshold is crossed, we also don’t know where we can use them without much worry, like we can with flexbox and grid now.

That “just use it” date is probably a decent ways off, but if you’re into the idea of polyfilling and being careful with progressive enhancement, I’d say the date for using Container Queries could be right now-ish. Looks to me like the polyfill script comes across the wire at 2.8kb, so it’s fairly trivial in size for something so important.

I suspect this polyfill will skyrocket usage of Container Queries in this coming year.

FOUC?

The fact that your styles only correctly apply after a JavaScript file is downloaded and executed puts sites into Flash of Unstyled Content (FOUC) territory. Here’s a video recording where I can see it on my own demo. I’m not sure there is a way around this other than intentionally delaying rendering, which is generally considered a no-no. Similar to loading web fonts, FOUC is probably a good thing as it means your content is never hidden or delayed, even if the shifts aren’t ideal. The FOUC should go away once browser support lands and the polyfill stops loading at all.

Have fun polyfilling container queries! I’d love to see more demos of it.

GitHub Repo for the Container Query Polyfill


A New Container Query Polyfill That Just Works originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , , ,
[Top]

Build Great Apps: Designer and Developer Collaboration Just Got Easier

(This is a sponsored post.)

We’re going to go over details on how designer ↔️ developer collaboration in AWS Amplify Studio can make app building easier, but let’s cover one of those things right at the top here.

Say your designer (or you!) like to work in Figma, the predominant tool in UI design these days. The whole design system is there, from colors and fonts, to atomic components like buttons and forms, to larger layout components like Cards and Collections. The designs in Figma evolve, and when they do, it’s a developer’s job to port the changes over to the site. What if… that didn’t need to be manual work? With AWS Amplify you can now suck over those updated Figma designs directly without any work other than reviewing and accepting the changes. That’s some pretty magical stuff that people have only theorized would be possible one day with design tooling. Now it’s here.

We will get more into that in a minute, I just wanted to make sure you caught that upfront.

Setting the Stage

Designers often struggle to work with developers and vice versa, even though both are usually passionate about delivering great app experiences. If you’re a designer, if you’ve ever had to hand off your visual wonder to a developer, then you know the dread that sometimes follows. Will your developer know how to implement your design fully? Will pixels get removed? Will I be happy with the final iteration?

If you’re a developer, you may think of design implementation and iteration as a tedious and manual process. You may not feel equipped to think about design, even though you do care about delighting your customers and end-users and growing your business.

There are a lot of tools that are intended to help facilitate the handoff process. However, most don’t fully address the implementation of the design.

The AWS Amplify team recently launched AWS Amplify Studio, a new visual development environment that lets front-end developers build full-stack apps with minimal coding while integrating Amplify’s powerful backend configuration and management capabilities. Amplify Studio helps automate the design implementation process, helping designers and developers to work better together, and ensuring that designs are implemented into the final product. Let’s dive in and check out the new capabilities.

Let’s build an app

In the previous post, we outlined Amplify Studio’s functionality. Now let’s walk through how to build and deploy a web app quickly, on AWS. Amplify Studio contains backend creation and management capabilities, simplifying setup and management of app backend infrastructure, such as database tables, user authentication, and file storage, without requiring cloud expertise.

Use the visual development environment to define a data model, user authentication, and file storage without backend expertise. Amplify Studio helps you do the heavy lifting. Let’s focus on the UI and data binding.

This whole demo was done by Ali Spittel, a Senior Developer Advocate for AWS Amplify.

For demonstration’s sake, imagine it’s a home rental app.

First, let’s create a data model so that we can link data to my UI components. Then create a component with an image of the rental, a name, price, and location. 

Then, we can save and deploy.

Next, let’s go over to Figma. Amplify Studio helps developers and UX designers work better together. In addition to the significant time it takes for developers to implement designs and iterations, often, the UX designs aren’t implemented accurately, which frustrates designers and leads to a sub-optimal end-user experience. With Amplify Studio, developers can import custom UI components created by their designers from Figma, or they can use the UI component library provided by Amplify Studio, and customize them to fit their style guide. 

The Amplify team launched a Figma file called the AWS Amplify UI Kit. 

The styling correlates with the Amplify UI library, which includes primitives, components (which are combinations of the primitives), cards, and collections.

Duplicate the file as a starter then edit away!  

You can customize the components however you wish. If you’re working with a UX designer, both of you can work with the Figma project and import the UX designs into Amplify Studio.

Amplify Studio’s Figma-to-code plugin lets me import UX designs directly from Figma, which are automatically translated into clean React code that can be further customized.

To import the UX components, paste the link to the Figma file and paste it into Amplify Studio.

All of my components from my Figma file will be imported into Amplify Studio. You’re able to review the imported components in Figma and decide whether to accept or reject them, as an added quality check.  If you are importing a design iteration, this helps you to see what the design changes are before you implement them.

This process helps me save time from not having to write thousands of lines of code, since Amplify Studio autogenerates it for me. It creates human readable, credible React code. Later, if needed, you can extend this code for additional control over the components.

We can then link the UI components to data from the data model, and can add child properties like rental price, name, and location. 

We can create a collection of components, and customize the layout. Say we want to display them as a list or a grid or change the direction that it goes in, adjust spacing, and so on. We can choose the data that is displayed on my page. 

Now, let’s need to integrate this into the app. Studio gives me code snippets to use to get my components into my app. 

Run the command amplify pull, which runs some code generation from all the Figma components we pulled into Amplify Studio. Then we can use these React components like you would any other in the code base.

We’ll have an Amplify directory with information about the API, and also have a models directory with data that is created on the front end. We also have a full UI components directory, which has all of the UI components that we created in Figma and imported into Amplify Studio. 

Say we’ve installed the Amplify JavaScript libraries and React UI components. We’ll use a couple of lines of code to configure Amplify for this app. We can use the Amplify Provider component which will be at the top level. It will allow us to have the right styling for my UI components. 

In the component below, we import the CSS file, then import the rental collection. Then we can see my React code for the app. This is the code that was generated by Amplify. 

Here are the properties that we can change in the Amplify UI documentation. 

We can also add overrides to items in the generated components. 

A lot of companies have their own style guides, and that’s great! We can use Amplify Studio to details of these style guides to Figma and ultimately our entire app.

There’s a new Figma plugin you can install: AWS Amplify Theme Editor. If you want to add a custom theme to the UI, you can use the plugin for that. This happens via CSS custom properties, with JavaScript objects, or design tokens within Figma using the Amplify UI Theme Editor plugin. You can use it to change the color palette, component colors, and brand colors. 

One of the great things about Amplify Studio is it works with the tools that designers and developers use, but helps to centralize and streamline their workflow while facilitating tighter collaboration. 

Designers can be assured that their UX designs and iterations are being fully implemented. Developers can be assured that they are incorporating design implementations as a part of their CI/CD process. As developers, we can save time not needing to translate designers’ ideas and changes into code, and we can focus on building a better app experience and the end users’ needs while making sure we haven’t missed the design details. 

Once this app is ready to deploy, we can host the app with Amplify Hosting. It offers hosting for any React, Vue, or Next.js web app with built-in continuous integration and continuous deployment (CI/CD) workflows, testing, pull request previews, and custom domains. 

Amplify Studio’s frontend UI capabilities are in preview (its app backend capabilities are generally available). You can go to the sandbox to try it out.

Calls to action

CSS-Tricks

, , , , , , ,
[Top]

“Just in Time” CSS

I believe acss.io is the first usage of “Atomic CSS” where the point of it is to be a compiler. You write CSS like this:

<div class="C(#fff) P(20px)">   text </div>

And it will generate CSS like:

.C(#333) {   color: #333; } .P(20px) {   padding: 20px; }

(Or something like that.)

The point is that it only generates the CSS that you actually need, because you asked for it, and no more. The result is far less CSS than you’d see in an average stylesheet.

That complication process is what has come to be known as “Just in Time” CSS.

The popular Tailwind framework supports it. It kind of flips the mental model of Tailwind on its head, to me. Rather than providing a huge pile of CSS utility classes to use — then “purging” what is unused — it only creates what it needs to begin with.

I’d say “Just in Time” is a concept that is catching on. I just saw Assembler CSS and it leans into it big time. Rather than classes, you do stuff like:

<div x-style="grid; gap:1rem; grid-rows:1; grid-cols:1; sm|grid-cols:3">   <button x-style="^button:red">Submit</button> </div>

I’m pretty torn on this stuff. Some part of me likes how you can get styling done without ever leaving your templates. And I especially like the extremely minimal CSS output since CSS is a blocking resource. Another part of me doesn’t like that it’s a limited abstraction of CSS itself, so you’re at the mercy of the tool to support things that CSS can do natively. It also makes HTML a bit harder to look at — although I certainly got over that with JSX inline event handlers and such.


The post “Just in Time” CSS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

When a Click is Not Just a Click

The click event is quite simple and easy to use; you listen for the event and run code when the event is fired. It works on just about every HTML element there is, a core feature of the DOM API.

As often the case with the DOM and JavaScript, there are nuances to consider. Some nuances with the click event are typically not much a concern. They are minor and probably most people would never even notice them in the majority of use cases.

Take, for example, the click event listening to the grandfather of interactive elements, the <button> element. There are nuances associated with button clicks and these nuances, like the difference between a “click” from a mouse pointer and “click” from the keyboard. Seen this way, a click is not always a “click” the way it’s typically defined. I actually have run into situations (though not many) where distinguishing between those two types of clicks comes in handy.

How do we distinguish between different types of clicks? That’s what we’re diving into!

First things first

The <button> element, as described by MDN, is simply:

The HTML element represents a clickable button, used to submit forms or anywhere in a document for accessible, standard button functionality. By default, HTML buttons are presented in a style resembling the platform the user agent runs on, but you can change buttons’ appearance with CSS.

The part we’ll cover is obviously the “anywhere in a document for accessible, standard button functionality” part of that description. As you may know, a button element can have native functionality within a form, for example it can submit a form in some situations. We are only really concerning ourselves over the basic clicking function of the element. So consider just a simple button placed on the page for specific functionality when someone interacts with it.

Consider that I said “interacts with it” instead of just clicking it. For historical and usability reasons, one can “click” the button by putting focus on it with tabbing and then using the Space or Enter key on the keyboard. This is a bit of overlap with keyboard navigation and accessibility; this native feature existed way before accessibility was a concern. Yet the legacy feature does help a great deal with accessibility for obvious reasons.

In the example above, you can click the button and its text label will change. After a moment the original text will reset. You can also click somewhere else within the pen, tab to put focus on the button, and then use Space or Enter to “click” it. The same text appears and resets as well. There is no JavaScript to handle the keyboard functionality; it’s a native feature of the browser. Fundamentally, in this example the button is only aware of the click event, but not how it happened.

One interesting difference to consider is the behavior of a button across different browsers, especially the way it is styled. The buttons in these examples are set to shift colors on its active state; so you click it and it turns purple. Consider this image that shows the states when interacting with the keyboard.

Keyboard Interaction States

The first is the static state, the second is when the button has focus from a keyboard tabbing onto it, the third is the keyboard interaction, and the fourth is the result of the interaction. With Firefox you will only see the first two and last states; when interacting with either Enter or Space keys to “click” it you do not see the third state. It stays with the second, or “focused”, state during the interaction and then shifts to the last one. The text changes as expected but the colors do not. Chrome gives us a bit more as you’ll see the first two states the same as Firefox. If you use the Space key to “click” the button you’ll see the third state with the color change and then the last. Interestingly enough, with Chrome if you use Enter to interact with the button you won’t see the third state with the color change, much like Firefox. In case you are curious, Safari behaves the same as Chrome.

The code for the event listener is quite simple:

const button = document.querySelector('#button');  button.addEventListener('click', () => {   button.innerText = 'Button Clicked!';      window.setTimeout(() => {     button.innerText = '"click" me';   }, 2000); });

Now, let’s consider something here with this code. What if you found yourself in a situation where you wanted to know what caused the “click” to happen? The click event is usually tied to a pointer device, typically the mouse, and yet here the Space or Enter key are triggering the same event. Other form elements have similar functionality depending on context, but any elements that are not interactive by default would require an additional keyboard event to work. The button element doesn’t require this additional event listener.

I won’t go too far into reasons for wanting to know what triggered the click event. I can say that I have occasionally ran into situations where it was helpful to know. Sometimes for styling reasons, sometimes accessibility, and sometimes for specific functionality. Often different context or situations provide for different reasons.

Consider the following not as The Way™ but more of an exploration of these nuances we’re talking about. We’ll explore handling the various ways to interact with a button element, the events generated, and leveraging specific features of these events. Hopefully the following examples can provide some helpful information from the events; or possibly spread out to other HTML elements, as needed.

Which is which?

One simple way to know a keyboard versus mouse click event is leveraging the keyup and mouseup events, taking the click event out of the equation.

Now, when you use the mouse or the keyboard, the changed text reflects which event is which. The keyboard version will even inform you of a Space versus Enter key being used.

Here’s the new code:

const button = document.querySelector('#button');  function reset () {   window.setTimeout(() => {     button.innerText = '"click" me';   }, 2000); }  button.addEventListener('mouseup', (e) => {   if (e.button === 0) {     button.innerText = 'MouseUp Event!';     reset();   } });  button.addEventListener('keyup', (e) => {   if (e.code === 'Space' || e.code === 'Enter') {     button.innerText = `KeyUp Event: $ {e.code}`;     reset();   } });

A bit verbose, true, but we’ll get to a slight refactor in a bit. This example gets the point across about a nuance that needs to be handled. The mouseup and keyup events have their own features to account for in this situation.

With the mouseup event, about every button on the mouse could trigger this event. We usually wouldn’t want the right mouse button triggering a “click” event on the button, for instance. So we look for the e.button with the value of 0 to identify the primary mouse button. That way it works the same as with the click event yet we know for a fact it was the mouse.

With the keyup event, the same thing happens where about every key on the keyboard will trigger this event. So we look at the event’s code property to wait for the Space or Enter key to be pressed. So now it works the same as the click event but we know the keyboard was used. We even know which of the two keys we’re expecting to work on the button.

Another take to determine which is which

While the previous example works, it seems like a bit too much code for such a simple concept. We really just want to know if the “click” came from a mouse or a keyboard. In most cases we probably wouldn’t care if the source of the click was either the Space or Enter keys. But, if we do care, we can take advantage of the keyup event properties to note which is which.

Buried in the various specifications about the click event (which leads us to the UI Events specification) there are certain properties assigned to the event concerning the mouse location, including properties such as screenX/screenY and clientX/clientY. Some browsers have more, but I want to focus on the screenX/screenY properties for the moment. These two properties essentially give you the X and Y coordinates of the mouse click in relation to the upper-left of the screen. The clientX/clientY properties do the same, but the origin is the upper-left of the browser’s viewport.

This trick relies on the fact that the click event provides these coordinates even though the event was triggered by the keyboard. When a button with the click event is “clicked” by the Space or Enter key it still needs to assign a value to those properties. Since there’s no mouse location to report, if it falls back to zero as the default.

Here’s our new code:

const button = document.querySelector('#button');  button.addEventListener('click', (e) => {   button.innerText = e.screenX + e.screenY === 0 || e.offsetX + e.offsetY === 0 ?     'Keyboard Click Event!' : 'Mouse Click Event!';      window.setTimeout(() => {     button.innerText = '"click" me';   }, 2000); });

Back to just the click event, but this time we look for those properties to determine whether this is a keyboard or mouse “click.” We take both screenX and screenY properties, add them together, and see if they equal zero; which makes for an easy test. The possibilities of the button being in the immediate upper-left of the screen to be clicked has to be quite low. It could be possible if one attempted to make such an effort of a pixel-perfect click in such an odd location, but I would think it’s a safe assumption that it won’t happen under normal circumstances.

Now, one might notice the added e.offsetX + e.offsetY === 0 part. I have to explain that bit…

Enter the dreaded browser inconsistencies

While creating and testing this code, the all-too-often problem of cross-browser support reared its ugly head. It turns out that even though most browsers set the screenX and screenY values on a keyboard-caused click event to zero, Safari decides to be different. It applies a proper value to screenX and screenY as if the button was clicked by a mouse. This throws a wrench into my code which is one of the fun aspects of dealing with different browsers — they’re made by different groups of different people creating different outcomes to the same use cases.

But, alas, I needed a solution because I didn’t necessarily want to rely only on the keyup event for this version of the code. I mean, we could if we wanted to, so that’s still an option. It’s just that I liked the idea of treating this as a potential learning exercise to determine what’s happening and how to make adjustments for differences in browsers like we’re seeing here.

Testing what Safari is doing in this case, it appears to be using the offsetX and offsetY properties in the event to determine the location of the “click” and then applying math to determine the screenX and screenY values. That’s a huge over-simplification, but it sort of checks out. The offset properties will be the location of the click based on the upper-left of the button. In this context, Safari applies zero to offsetX and offsetY, which would obviously be seen as the upper-left of the button. From there it treats that location of the button as the determination for the screen properties based on the distance from the upper-left of the button to the upper-left of the screen.

The other usual browsers technically also apply zero to offestX and offsetY, and could be used in place of screenX and screenY. I chose not to go that route. It’s certainly possible to click a button that happens to be at the absolute top-left of the screen is rather difficult while clicking the top-left of a button. Yet, Safari is different so the tests against the screen and offsets is the result. The code, as written, hopes for zeroes on the screen properties and, if they are there, it moves forward assuming a keyboard-caused click. If the screen properties together are larger then zero, it checks the offset properties just in case. We can consider this the Safari check.

This is not ideal, but it wouldn’t be the first time I had to create branching logic due to browser inconsistencies.

In the hope that the behavior of these properties will not change in the future, we have a decent way to determine if a button’s click event happened by mouse or keyboard. Yet technology marches on providing us new features, new requirements, and new challenges to consider. The various devices available to us has started the concept of the “pointer” as a means to interact with elements on the screen. Currently, such a pointer could be a mouse, a pen, or a touch. This creates yet another nuance that we might want to be consider; determining the kind of pointer involved in the click.

Which one out of many?

Now is a good time to talk about Pointer Events. As described by MDN:

Much of today‘s web content assumes the user’s pointing device will be a mouse. However, since many devices support other types of pointing input devices, such as pen/stylus and touch surfaces, extensions to the existing pointing device event models are needed. Pointer events address that need.

So now let’s consider having a need for knowing what type of pointer was involved in clicking that button. Relying on just the click event doesn’t really provide this information. Chrome does have an interesting property in the click event, sourceCapabilities. This property in turn has a property named firesTouchEvents that is a boolean. This information isn’t always available since Firefox and Safari do not support this yet. Yet the pointer event is available much everywhere, even IE11 of all browsers.

This event can provide interesting data about touch or pen events. Things like pressure, contact size, tilt, and more. For our example here we’re just going to focus on pointerType, which tells us the device type that caused the event.

Clicking on the button will now tell you the pointer that was used. The code for this is quite simple:

const button = document.querySelector('#button');  button.addEventListener('pointerup', (e) => {   button.innerText = `Pointer Event: $ {e.pointerType}`;      window.setTimeout(() => {     button.innerText = '"click" me';   }, 2000); });

Really, not that much different than the previous examples. We listen for the pointerup event on the button and output the event’s pointerType. The difference now is there is no event listener for a click event. So tabbing onto the button and using space or enter key does nothing. The click event still fires, but we’re not listening for it. At this point we only have code tied to the button that only responds to the pointer event.

That obviously leaves a gap in functionality, the keyboard interactivity, so we still need to include a click event. Since we’re already using the pointer event for the more traditional mouse click (and other pointer events) we have to lock down the click event. We need to only allow the keyboard itself to trigger the click event.

The code for this is similar to the “Which Is Which” example up above. The difference being we use pointerup instead of mouseup:

const button = document.querySelector('#button');  function reset () {   window.setTimeout(() => {     button.innerText = '"click" me';   }, 2000); }  button.addEventListener('pointerup', (e) => {   button.innerText = `Pointer Event: $ {e.pointerType}`;   reset(); });  button.addEventListener('click', (e) => {   if (e.screenX + e.screenY === 0 || e.offsetX + e.offsetY === 0) {     button.innerText = 'Keyboard  ||Click Event!';     reset();   } });

Here we’re using the screenX + screenY (with the additional offset check) method to determine if the click was caused by the keyboard. This way a mouse click would be handled by the pointer event. If one wanted to know if the key used was space or enter, then the keyup example above could be used. Even then, the keyup event could be used instead of the click event depending on how you wanted to approach it.

Anoher take to determine which one out of many

In the ever-present need to refactor for cleaner code, we can try a different way to code this.

Yep, works the same as before. Now the code is:

const button = document.querySelector('#button');  function btn_handler (e) {   if (e.type === 'click' && e.screenX + e.screenY > 0 && e.offsetX + e.offsetY > 0) {     return false;   } else if (e.pointerType) {     button.innerText = `Pointer Event: $ {e.pointerType}`;   } else if (e.screenX + e.screenY === 0) {     button.innerText = 'Keyboard Click Event!';   } else {     button.innerText = 'Something clicked this?';   }      window.setTimeout(() => {     button.innerText = '"click" me';   }, 2000); }  button.addEventListener('pointerup', btn_handler); button.addEventListener('click', btn_handler);

Another scaled down version to consider: this time we’ve reduced our code down to a single handler method that both pointerup and click events call. First we detect if the mouse “click” caused the event; if it does, we wish to ignore it in favor of the pointer event. This is checked with a test opposite of the keyboard test; is the sum of screenX and screenY larger than zero? This time there’s an alteration to the offset check by doing the same as the screen test, is the sum of those properties larger than zero as well?

Then the method checks for the pointer event, and upon finding that, it reports which pointer type occurred. Otherwise, the method checks for keyboard interactions and reports accordingly. If neither of those are the culprit, it just reports that something caused this code to run.

So here we have a decent number of examples on how to handle button interactions while reporting the source of those interactions. Yet, this is just one of the handful of form elements that we are so accustomed to using in projects. How does similar code work with other elements?

Checking checkboxes

Indeed, similar code does work very much the same way with checkboxes.

There are a few more nuances, as you might expect by now. The normal usage of <input type="checkbox"> is a related label element that is tied to the input via the for attribute. One major feature of this combination is that clicking on the label element will check the related checkbox.

Now, if we were to attach event listeners for the click event on both elements, we get back what should be obvious results, even if they are a bit strange. For example, we get one click event fired when clicking the checkbox. If we click the label, we get two click events fired instead. If we were to console.log the target of those events, we’ll see on the double event that one is for the label (which makes sense as we clicked it), but there’s a second event from the checkbox. Even though I know these should be the expected results, it is a bit strange because we’re expecting results from user interactions. Yet the results include interactions caused by the browser.

So, the next step is to look at what happens if we were to listen for pointerup, just like some of the previous examples, in the same scenarios. In that case, we don’t get two events when clicking on the label element. This also makes sense as we’re no longer listening for the click event that is being fired from the checkbox when the label is clicked.

There’s yet another scenario to consider. Remember that we have the option to put the checkbox inside the label element, which is common with custom-built checkboxes for styling purposes.

<label for="newsletter">   <input type="checkbox" />   Subscribe to my newsletter </label>

In this case, we really only need to put an event listener on the label and not the checkbox itself. This reduces the number of event listeners involved, and yet we get the same results. Clicks events are fired as a single event for clicking on the label and two events if you click on the checkbox. The pointerup events do the same as before as well, single events if clicking on either element.

These are all things to consider when trying to mimic the behavior of the previous examples with the button element. Thankfully, there’s not too much to it. Here’s an example of seeing what type of interaction was done with a checkbox form element:

This example includes both types of checkbox scenarios mentioned above; the top line is a checkbox/label combination with the for attribute, and the bottom one is a checkbox inside the label. Clicking either one will output a message below them stating which type of interaction happened. So click on one with a mouse or use the keyboard to navigate to them and then interact with Space or Enter; just like the button examples, it should tell you which interaction type causes it.

To make things easier in terms of how many event listeners I needed, I wrapped the checkboxes with a container div that actually responds to the checkbox interactions. You wouldn’t necessarily have to do it this way, but it was a convenient way to do this for my needs. To me, the fun part is that the code from the last button example above just copied over to this example.

const checkbox_container = document.querySelector('#checkbox_container'); const checkbox_msg = document.querySelector('#checkbox_msg');  function chk_handler (e) {   if (e.type === 'click' && e.screenX + e.screenY > 0 && e.offsetX + e.offsetY > 0) {     return false;   } else if (e.pointerType) {     checkbox_msg.innerText = `Pointer Event: $ {e.pointerType}`;   } else if (e.screenX + e.screenY === 0) {     checkbox_msg.innerText = 'Keyboard Click Event!';   } else {     checkbox_msg.innerText = 'Something clicked this?';   }      window.setTimeout(() => {     checkbox_msg.innerText = 'waiting...';   }, 2000); }  checkbox_container.addEventListener('pointerup', chk_handler); checkbox_container.addEventListener('click', chk_handler);

That means we could possibly have the same method being called from the the various elements that need the same detecting the pointer type functionality. Technically, we could put a button inside the checkbox container and it should still work the same. In the end it’s up to you how to implement such things based on the needs of the project.

Radioing your radio buttons

Thankfully, for radio button inputs, we can still use the same code with similar HTML structures. This mostly works the same because checkboxes and radio buttons are essentially created the same way—it’s just that radio buttons tend to come in groups tied together while checkboxes are individuals even in a group. As you’ll see in the following example, it works the same:

Again, same code attached to a similar container div to prevent having to do a number of event listeners for every related element.

When a nuance can be an opportunity

I felt that “nuance” was a good word choice because the things we covered here are not really “issues” with the typical negative connotation that word tends to have in programming circles. I always try to see such things as learning experiences or opportunities. How can I leverage things I know today to push a little further ahead, or maybe it’s time to explore outward into new things to solve problems I face. Hopefully, the examples above provide a somewhat different way to look at things depending on the needs of the project at hand.

We even found an opportunity to explore a browser inconsistency and find a workaround to that situation. Thankfully we don’t run into such things that much with today’s browsers, but I could tell you stories about what we went through when I first started web development.

Despite this article focusing more on form elements because of the click nuance they tend to have with keyboard interactions, some or all of this can be expanded into other elements. It all depends on the context of the situation. For example, I recall having to do multiple events on the same elements depending on the context many times; often for accessibility and keyboard navigation reasons. Have you built a custom <select> element to have a nicer design than the standard one, that also responds to keyboard navigation? You’ll see what I mean when you get there.

Just remember: a “click” today doesn’t always have to be what we think a click has always been.


The post When a Click is Not Just a Click appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Just How Niche is Headless WordPress?

I wonder where headless WordPress will land. And by “headless” I mean only using the WordPress admin and building out the user-facing site through the WordPress REST API rather than the traditional WordPress theme structure.

Is it… big? The future of WordPress? Or relatively niche?

Where’s the demand?

Certainly, there is demand for it. I know plenty of people doing it. For instance, Gatsby has a gatsby-source-wordpress plugin that allows you to source content from a WordPress site in a way that consumes the WordPress REST API and caches it as GraphQL for use in a React-powered Gatsby site. It has been downloaded 59k times this month and 851k times overall, as I write. That’s a healthy chunk of usage for one particular site-building technology. Every use case there is technically using WordPress headless-ly. If you’re interested in this, here’s Ganesh Dahal digging deep into it.

What is going headless and improve to?

The Gatsby integration makes a solid case for why anyone would consider a headless WordPress site. I’ll get to that.

Many advocate the main reason is architectural. It de-couples the back end from the front end. It tears down the monolith. As a de-coupled system, the back and front ends can evolve independently. And yet, I’m less hot on that idea as years go by. For example, I’d argue that the chances of simply ripping out WordPress and replace it with another CMS in a headless setup like this is easier said than done. Also, the idea that I’m going to use the WordPress API not just to power a website, but also a native reading app, and some digital internet-connected highway billboard or something is not a use case that’s exploding in popularity as far as I can tell.

The real reason I think people reach for a WordPress back end for a Gatsby-driven front end is essentially this: they like React. They like building things from components. They like the fast page transitions. They like being able to host things somewhere Jamstack-y with all the nice developer previews and such. They like the hot module reloading. They like Prettier and JSX. They just like it, and I don’t blame them. When you enjoy that kind of developer experience, going back to authoring PHP templates where it takes manually refreshing the browser and maintaining some kind of hand-rolled build process isn’t exactly enticing.

Frontity is another product looking to hone in on React + WordPress. Geoff and Sarah shared how to do all this last year on the Vue/Nuxt side.

But will headless WordPress become more popular than the traditional theming model of WordPress that’s based on PHP templates that align to an explicit structure? Nah. Well, maybe it would if WordPress itself champions the idea and offers guidance, training, and documentation that make it easier for developers to adopt that approach. I’d buy into it if WordPress says that a headless architecture is the new course of direction. But none of those things are true. Consequently, to some degree, it’s a niche thing.

Just how niche is headless?

WP Engine is a big WordPress host and has a whole thing called Atlas. And that effort definitely looks like they are taking this niche market seriously. I’m not 100% sure what Atlas all is, but it looks like a dashboard for spinning up sites with some interesting looking code-as-config. One of the elephants in the room with headless WordPress is that, well, now you have two websites to deal with. You have wherever you are hosting and running WordPress, and wherever you are hosting and running the site that consumes the WordPress APIs. Maybe this brings those two things together somehow. The deploy-from-Git-commits thing is appealing and what I’d consider table stakes for modern hosting these days.

Another reason people are into headless WordPress is that the end result can be static, as in, pre-generated HTML pages. That means the server for your website can be highly CDN-ized, as it’s literally only static assets that are instantly available to download. There’s no PHP or database for server-side rendering things, which can be slow (and, to be fair, dealt with) since it adds a process ahead of rendering.

What’s “the WordPress way” for going headless?

I’d lump any service that builds a static version of your WordPress site into the headless WordPress bucket. That’s because, ultimately, those sites are using WordPress APIs to build those static files, just like Gatsby or whatever else would do.

That’s what Strattic does. They spin up a WordPress site for you that they consider staging. You do your WordPress work there, then use their publish system to push a static version of your site to production. That’s compelling because it solves for something that other headless WordPress usage doesn’t: just doing things the WordPress way.

For example, custom WordPress blocks or plugins might produce output that not only contains custom HTML, but CSS and JavaScript as well. Imagine a “carousel” block or plugin. That carousel isn’t going to work if all you’re doing is grabbing the post content from an API and dunking it onto a page. You’ll either need to go extract the CSS and JavaScript from elsewhere and include it, or somehow just know that isn’t how you do things anymore. You might attach the images as metadata somehow, pull them client-side, and do your own implementation of a carousel. With Strattic, theoretically, it’ll work as the HTML, CSS, and JavaScript is still present on the static site. But notably, you don’t have PHP, so Strattic had to hand-build form integrations, they use client-side Algolia for search, Disqus for comments, etc., because there is no server-side language available.

Shifter is another player here. It’s similar to Strattic where you work on your site in the WordPress admin, then publish to a static site. I believe Shifter even spins your WordPress site down when it’s not in use, which makes sense since the output is static and there is no reason a server with PHP and MySQL needs to be running. As an alternative, Shifter has a headless-only WordPress setup that presumably stays spun up all the time for outside API usage.

It’s fun to think about all this stuff

But as I do, I realize that the ideas and discussions around headless WordPress are mostly focused on the developer. WordPress has this huge market of people who just are not developers. Yet, they administer a WordPress site, taking advantage of the plugin and theme ecosystem. That’s kinda cool, and it’s impressive that WordPress serves both markets so well. There’s just a heck of a lot more WordPress site owners who aren’t developers than those who are, I reckon, so that alone will keep headless WordPress from being anything more than a relatively niche concept for some time. But, ya know, if they wanna put GraphQL in core, I’ll still take it kthxbye.


The post Just How Niche is Headless WordPress? appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

I Just Wanna Make Sure You Understand the WordPress.com Business Plan

WordPress.com is where you go to use WordPress that is completely hosted for you. You don’t have to worry about anything but building your site. There is a free plan to get started with, and paid plans that offer more features. The Business plan is particularly interesting, and my guess is that most people don’t fully understand everything that it unlocks for you, so let’s dig into that.

You get straight up SFTP access to your site.

Here’s me using Transmit to pop right into one of my sites over SFTP.

What this means is that you can do local WordPress development like you normally would, then use real deployment tools to kick your work out to production (which is your WordPress.com site). That’s what I do with Buddy. (Here a screencast demonstrating the workflow.)

That means real control.

I can upload and use whatever plugins I want. I can upload and use whatever themes I want. The database too — I get literal direct MySQL access.

I can even manage what PHP version the site uses. That’s not something I’d normally even need to do, but that’s just how much access there is.

A big jump in storage.

200 GB. You’ll probably never get anywhere near that limit, unless you are uploading video, and if you are, now you’ve got the space to do it.

Backups you’ll probably actually use.

You don’t have to worry about anything nasty happening on WordPress.com, like your server being hacked and losing all your data or anything. So in that sense, WordPress.com is handling your backups for you. But with the Business plan, you’ll see a backup log right in your dashboard:

That’s a backup of your theme, data, assets… everything. You can download it anytime you like.

The clutch feature? You can restore things to any point in time with the click of a button.

Powered by a global CDN

Not every site on WordPress.com is upgraded to the global CDN. Yours will be if it’s on the Business plan. That means speed, and speed is important for every reason, including SEO. And speaking of SEO tools, those are unlocked for you on the Business plan as well.

Some of the best themes unlock at the Premium/Business plan level.

You can buy them one-off, but you don’t have to if you’re on the Business plan because it opens the door for more playing around. This Aquene theme is pretty stylish with a high-end design:

It’s only $ 300/year.

So it’s not ultra-budget hosting, but the price tag is a lot less if you consider all the things we covered here and how much they cost if you were to cobble something together yourself. And we didn’t even talk about support, which is baked right into the plan.

Hosting, backups, monitoring, performance, security, plugins, themes, and support — toss in a free year or domain registration, and that’s a lot of website for $ 300.

They have less expensive plans as well. But the Business plan is the level where serious control, speed, and security kick in.


The post I Just Wanna Make Sure You Understand the WordPress.com Business Plan appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , ,
[Top]

That’s Just How I Scroll

How do you know a page (or any element on that page) scrolls? Well, if it has a scrollbar, that’s a pretty good indication. You might still have to scrapple with your client about “the fold” or whatever, but I don’t think anyone is confused at what a scrollbar is or what it indicates.

But let’s say there is no scrollbar. That’s super common. macOS hides scrollbars by default and only shows them during scroll. Most mobile browsers don’t have scrollbars, even if you attempt to force them with something like overflow: scroll;.

Why does this matter? If you don’t know an area is scrollable, you might miss out on important content or functionality.

I regularly think about the Perfectly Cropped story from Tyler Hall. There is a screen on iOS that has important functionality you need to scroll down to, but there is no indicator whatsoever that you can scroll there.

The result of that was Tyler’s mom literally not being able to find functionality she was used to. Not great.

There is an elaborate way to detect visible scrollbars and force them to be visible, but something about that rubs me the wrong way. It doesn’t honor a user’s preference (assuming it is the user’s preference), requires DOM manipulation tests, and uses vendor-prefixed CSS (which will probably live a long time, but has been standardized now, so maybe not forever).

I enjoy these approaches and by Chris Smith and his thinking:

My favorite are the shadow-based techniques. To me an inset shadow is a really clear indicator, as it makes it appear that content is flowing underneath and the shadow follows an edge that as a hint that you can scroll in that direction. Plus, you’ve got CSS control there so I’d think it could match whatever UI situation you’re in fairly easily.

It should be known though that it can be done entirely in CSS though, no JavaScript, and is one of the great CSS tricks.


The post That’s Just How I Scroll appeared first on CSS-Tricks.

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

CSS-Tricks

, ,
[Top]

Just another +1 for subgrid

I’d say 85% of my grid usage is in one of these two categories…

  1. I just need some pretty basic (probably equal width) columns that ends up being something like like grid-template-columns: repeat(3, minmax(0, 1fr)); to be safe.
  2. Actually doing some real layout where five minutes in I realize I’d really like subgrid.

Subgrid? It’s a nice intuitive way to have a child element on the grid inherit relevant grid lines from the parent grid.

Here’s a great recent video from Rachel Andrew covering it. Last year, we linked up her talk on the same! It’s such a clutch feature and I wish we could rely on it cross-browser. Right now, Firefox is the only one that has it. (Chrome issue, Safari issue)

In my recent video, right about at 20 minutes, I realize subgrid would make even a fairly simple layout much nicer, like removing the need for variables or resorting to magic numbers.

The post Just another +1 for subgrid appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Flexbox-like “just put elements in a row” with CSS grid

It occurred to me while we were talking about flexbox and gap that one reason we sometimes reach for flexbox is to chuck some boxes in a row and space them out a little.

My brain still reaches for flexbox in that situation, and with gap, it probably will continue to do so. It’s worth noting though that grid can do the same thing in its own special way.

Like this:

.grid {   display: grid;   gap: 1rem;   grid-auto-flow: column; }

They all look equal width there, but that’s only because there is no content in them. With content, you’ll see the boxes start pushing on each other based on the natural width of that content. If you need to exert some control, you can always set width / min-width / max-width on the elements that fall into those columns — or, set them with grid-template-columns but without setting the actual number of columns, then letting the min-content dictate the width.

.grid {   display: grid;   gap: 1rem;   grid-auto-flow: column;   grid-template-columns: repeat(auto-fit, minmax(min-content, 1fr)); }

Flexible grids are the coolest.

Another thought… if you only want the whole grid itself to be as wide as the content (i.e. less than 100% or auto, if need be) then be aware that display: inline-grid; is a thing.

The post Flexbox-like “just put elements in a row” with CSS grid appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]