I, Dave Rupert, a person who cares about web performance, a person who reads web performance blogs, a person who spends lots of hours trying to keep up on best practices, a person who co-hosts a weekly podcast about making websites and speak with web performance professionals… somehow goofed and added 33 SECONDS to their page load.
This stuff is hard even when you care a lot. The 33 seconds came from font preloading rather than the one-line wonder of font-display.
I also care about making fast websites, but mine aren’t winning any speed awards because I’ll take practical and maintainable over peak performance any day. (Sorry, world)
I’m sure most of us have used the ol’ Wayback Machine to access some site that’s gone offline. I don’t actually know how it decides what sites to archive and when, but you can tell it to save pages. There is UI for it right on its homepage.
Also, there is a little trick…
Typing https://t.co/R5w2bQZKWz in front of any URL saves that content in the Wayback Machine forever. Nasty tweet? Type https://t.co/R5w2bQZKWz in front of the URL, and archive it forever. Hat tip: @t.
Brian Kardell was given access to some kind of secret API that allows submission of pages, and he built a public service around it anyone can use. Here’s his blog post on it. You hit the endpoint with some JSON in your choice of a couple of formats and it’ll do the rest. The idea is that other systems would use this for submissions. Imagine a WordPress plugin that hit it when you hit submit or update on a post. Or a Netlify build plugin that pinged this as you deployed.
I’m not entirely sure what the difference is between this service and the URL technique from Zeldman’s tweet, but I gotta imagine an API-based submission service is more reliable.
The big idea is that you’re telling this service to archive your page forever, which is the mission of the Internet Archive. So, should your site ever go away, the content lives on. So you’d better want that before you do this!
Ever wanted to easily convert an image to a grayscale image on your phone? I do sometimes, and that’s why I build a demo using the Web Share Target API to achieve exactly that.
For this I used the Service Worker way to handle the data. Once the data is received on the client, I use drawImage from canvas to draw the image in canvas, use the grayscale filter to convert it to a grayscale image and output the final image.
So you „install” the little microsite like a PWA, then you natively „share” an image to it and it comes back edited. Clever. Android on Chrome only at the moment.
Reminds me of this „Browser Functions” idea in reverse. That was a server that did things a browser can do, this is a browser doing things a server normally does.
Neumorphism (aka neomorphism) is a relatively new design trend and a term that’s gotten a good amount of buzz lately. It’s aesthetic is marked by minimal and real-looking UI that’s sort of a new take on skeuomorphism — hence the name. It got its name in a UX Collective post from December 2019, and since then, various design and development communities have been actively discussing the trend, usually with differing opinions. Chris poked fun at it on Twitter. Adam Giebl created an online generator for it. Developers, designers, and UX specialists are weighing in on the topic of aesthetics, usability, accessibility, and practicality of this design trend.
Clearly, it’s stricken some sort of chord in the community.
Let’s dip our toes into the neumorphism pool, showcasing the various neumorphic effects that can be created using our language of choice, CSS. We’ll take a look at both the arguments for and against the style and weigh how it can be used in a web interface.
Neumorphism as a user interface
We’ve already established that the defining quality of neumorphism is a blend of minimalism and skeuomorphism. And that’s a good way to look at it. Think about the minimal aesthetic of Material Design and the hyper-realistic look of skeuomorphism. Or, think back to Apple’s design standards circa 2007-12 and compare it to the interfaces it produces today.
Nine years of Apple Calendar! The image on the left is taken from 2011 and exhibits the look and feel of a real, leather-bound journal, said to be inspired by one on Steve Jobs’ personal yacht. The right is the same app as shown today in 2020, bearing a lot less physical inspiration with a look and feel we might describe as “flat” or minimal.
If we think about Apple’s skeuomorphic designs from earlier in the century as one extreme and today’s minimal UI as another, then we might consider neumorphism as something in the middle.
Alexander Plyuto has championed and evolved neomorphic designs on his Dribbble account. (Source)
Neumorphic UI elements look like they’re connected to the background, as if the elements are extruded from the background or inset into the background. They’ve been described by some as “soft UI” because of the way soft shadows are used to create the effect.
Another way to understand neumorphic UI is to compare it to Material Design. Let’s use a regular card component to draw a distinction between the two.
Notice how the Material Design card (left) looks like it floats above the background, while the neumorphic variation(right) appears to be pushed up through the background, like a physical protrusion.
Let’s break down the differences purely from a design standpoint.
Quality
Material Design
Neomorphism
Shadows
Elements have a single or multiple dark shadows around them.
Elements have two shadows: one light and one dark.
Background colors
An element’s background color can be different than the background color of the parent element.
Background colors must be the same (or very similar) as the background color of the parent element.
Edges
Elements can be rounded or squared.
Rounded edges are a defining quality.
Borders
There are no hard rules on borders. Using them can help prevent elements that look like they are floating off the screen.
Elements can have an optional subtle border to improve contrast and make the edges a bit sharper
That should draw a pretty picture of what we’re talking about when we refer to neumorphism. Let’s move on to how it’s implemented in CSS.
Neumorphism and CSS
Creating a neumorphic interface with CSS is seemingly as easy as applying a regular box-shadow property on any element, but it’s more nuanced than that. The distinctiveness of a neumorphic UI comes from using multiple box-shadow and background-color values to achieve different types of effects and variations.
Neumorphic box shadows
Let’s do a quick refresher on the box-shadow property first so we can get a better understanding. Here’s the syntax:
Horizontal offset: A positive value offsets shadow to the right, while a negative value offsets it to the left.
Vertical offset: A positive value offsets shadow upwards, while a negative value offsets it downwards.
Blur Radius: The length of the shadow. The longer the length, the bigger and lighter the shadow becomes. There are no negative values.
Spread Radius: This is another length value, where larger values result in bigger, longer shadows.
Color: This defines the shadow’s color, just as we’d do for the CSS color property.
Inset: The default value (initial) results in a drop shadow. Using the inset value moves the shadow inside the frame of the element, resulting in an inner shadow.
We can apply multiple shadows using comma-separated box-shadow values. Up to four values can be concatenated, one for each side of the box.
box-shadow: 20px 20px 50px #00d2c6,
-30px -30px 60px #00ffff;
The following shows the box-shadow property values for a neumorphic UI element. Individual offset, blur and opacity values can be adjusted to be higher or lower, depending on the size of an element and the intensity of the effect that you’re trying to achieve. For neumorphism, it’s required to keep the shadows soft and low contrast.
CodePen Embed Fallback
As we’ve mentioned before, a core part of neumorphic elements is the use of two shadows: a light shadow and a dark shadow. That’s how we get that sort of “raised” effect and we can create variations by changing the “light source” of the shadows.
Two positive and two negative offset values need to be set. Taking this into account, we get the following four combinations, simply by changing the placement of each shadow.
CodePen Embed Fallback
Let’s use CSS variables to keep the values abstract and better understand the variations.
We can use inset shadows to create yet more variations. Unlike drop shadows that make an element appear to be raised from beneath the background, an inset shadow gives the appearance that the element is being pressed into it.
We can change if the element is extruded from the background or inset into the background by applying the initial (not apply the option at all) or inset, respectively.
Let’s keep our light source as the top left and only toggle the inset option to see the difference.
CodePen Embed Fallback
Background colors
You might have noticed that the box-shadow values change the look of the edges of a neumorphic element. So far, we haven’t changed the background-color because it needs to be transparent or have the same (or similar) color as a background color of an underlying element, such as the element’s parent.
We can use both solid and gradient backgrounds. Using a solid color background on the element can create a flat surface sort of look, when that solid color is the same as the color of the underlying element.
On the other hand, using subtle gradients can change how the surface is perceived. As with the box-shadow property, there is alight and a dark value in a gradient. The gradient angle needs to be adjusted to match the light source. We have the following two variations when using gradients:
Convex surface variation: The surface curves outwards where the gradient’s lighter section is aligned with the shadow’s lighter section, and the gradient’s darker section is aligned to the shadow’s darker section.
Concave surface variation: The surface curves inward where the gradient’s lighter section is aligned to the shadow’s darker section, and the gradient’s darker section is aligned to the shadow’s lighter section.
Let’s see how Neumorphism performs when applied to a simple button. The main characteristic of a neumorphic interface is that it blends with the background and does so by having a similar or same background color as the underlying element. The main purpose of many buttons, especially a primary call-to-action, is to stand out as much as possible, usually with a prominent background color in order to separate it from other elements and other buttons on the page.
The background color constraint in neumorphism takes away that convenience. If the background color of the button matches the background color of what it’s on top of, we lose the ability to make it stand out visually with a unique color.
We can try and adjust text color, add a border below the text, add an icon or some other elements to increase the visual weight to make it stand out, etc. Whatever the case, a solid background color on a neumorphic button seems to stand out more than a gradient. Plus, it can be paired with an inset shadow on the active state to create a nice “pressed” effect.
Even though the solid color on a neumorphic button calls more attention than a gradient background, it still does not beat the way an alternate color makes a button stand out from other elements on the page.
Taking some inspiration from the real-world devices, I’ve created the following examples as an attempt to improve on the neumorphic button and toggle concept. Although the results look somewhat better, the regular button still provides a better UX, has much fewer constraints, is more flexible, is simpler to implement, and does a better job overall.
CodePen Embed Fallback
The first example was inspired by a button on my router that extrudes from the device and has a sticker with an icon on it. I added a similar “sticker” element with a solid color background as well as a slight inset to add more visual weight and make it stand out as closely as possible to the ideal button. The second example was inspired by a car control panel where the button would light up when it’s in an active (pressed) state.
Let’s take a look at some more HTML elements. One of the downsides of neumorphism that has been pointed out is that it shouldn’t be applied to elements that can have various states, like inputs, select elements, progress bars, and others. These states include:
UX and accessibility rules require some elements to look different in each of their respective validation states and user interaction states. Neumorphism constraints and restrictions severely limit the customization options that are required to achieve the different styles for each possible state. Variations will be very subtle and aren’t possibly able to cover every single state.
Everything looks like a button! Notice how the input and button look similar and how the progress bar looks like a scrollbar or a really wide toggle.
It’s hard to see which elements are clickable! Even though this is the simplest possible example that showcases the issue, we could have added extra elements and styles to try and mitigate the issues. But as we’ve seen with the button example, some other types of elements would still perform better in terms of UX and accessibility.
It’s important to notice that Neumorphic elements also take more space (inside padding and outside margin) due to the shadow and rounded corners. A neumorphic effect wouldn’t look so good on a small-sized element simply because the visual effects consume the element.
The ideal element for neumorphism are cards, or any other static container element that doesn’t have states based on user interaction (e.g. hover, active and disabled) or validation (e.g. error, warning, and success).
In his highly critical article on neumorphism, Michal Malewicz (who helped coin “Neumorphism” as a term) suggests adding Neumorphic effects to the cards that already look good without it.
So the only way it works OK is when the card itself has the right structure, and the whole extrusion is unnecessary for hierarchy.
See?
It works well when it can be removed without any loss for the product.
Accessibility and UX
We’ve seen which elements work well with neumorphism, but there are some important rules and restrictions to keep in mind when adding the effect to elements.
First is accessibility. This is a big deal and perhaps the biggest drawback to neumorphism: color contrast.
Neumorphic UI elements rely on multiple shadows that help blend the element into the background it is on. Using subtle contrasts isn’t actually the best fit for an accessible UI. If a contrast checker is scanning your UI, it may very well call you out for not having high enough contrast between the foreground and background because shadows don’t fit into the equation and, even if they did, they’d be too subtle to make much of a difference.
Here are some valid criticisms about the accessibility of a neumorphic design:
Users with color blindness and poor vision would have difficulty using it due to the poor contrast caused by the soft shadows.
Page hierarchy is difficult to perceive when the effect is overused on a page. No particular element stands out due to the background color restrictions.
Users can get confused when the effect is overused on a page. Due to the extrusion effect, it’s difficult to determine which elements users can interact with and which are static.
In order to achieve a good contrast with the shadow, the background color of what a neumorphic element sits on shouldn’t get too close to the edges of RGB extremes (white and black).
Now let’s talk UX for a moment. Even though Neumorphic UI looks aesthetically pleasing, it shouldn’t be a dominant style on a page. If used too often, the UI will have an overwhelmingly plastic effect and the visual hierarchy will be all out of whack. Ae page could easily lose its intended structure when directing users to the most important content or to the main flow.
My personal take is that neumorphism is best used as an enhancement to another style. For example, it could be paired with Material Design in a way that draws distinctions between various component styles. It’s probably best to use it sparsely so that it adds a fresh alternative look to something on the screen — there’s a diminishing return on its use and it’s a good idea to watch out for it.
Here’s an example where neumorphic qualities are used on card elements in combination with Materialize CSS:
CodePen Embed Fallback
See, it can be pretty nice when used as an accent instead of an entire framework.
That’s a wrap
So that was a deep look at neumorphism. We broke down what makes the style distinct from other popular styles, looked at a few ways to re-create the effect in CSS, and examined the implications it has on accessibility and user experience.
In practice, a full-scale neumorphic design system probably cannot be used on a website. It’s simply too restrictive in what colors can be used. Plus, the fact that it results in soft contrasts prevents it from being used on interactive elements, like buttons and toggle elements. Sure, it’s aesthetically-pleasing, modern and unique, but that shouldn’t come at the expense of usability and accessibility. It should be used sparsely, ideally in combination with another design system like Material Design.
Neumorphism is unlikely to replace the current design systems we use today (at least in my humble opinion), but it may find its place in those same design systems as a fresh new alternative to existing cards and static container styles.
That might look a little weird to us folks who are so used to the CSS syntax, where it is font-size (not fontSize), margin-bottom (not marginBottom), and semi-colons (not commas).
That’s not JSX (or React or whatever) being weird — that’s just how styles are in JavaScript. If you wanted to set the font-size from any other JavaScript, you’d have to do:
div.style.fontSize = "16px";
I say that, but other APIs do want you to use the CSS syntax, like:
There are also lots of CSS-in-JS libraries that either require or optionally support setting styles in this object format. I’ve even heard that with libraries that support both the CSS format (via template literals) and the object format (e.g. Emotion), that some people prefer the object syntax over the CSS syntax because it feels more at home in the surrounding JavaScript and is a bit less verbose when doing stuff like logic or injecting variables.
Anyway, the actual reason for the post is this little website I came across that converts the CSS format to the object format. CSS2JS:
Definitely handy if you had a big block of styles to convert.
[…] with WordPress child themes, you are all but guaranteed to get into the weeds of specificity, hunting around theme stylesheets that you didn’t author, trying to figure out what existing declaration is preventing you from applying a new style, and then figuring out the least specificity you need to override it, and then thinking “Maybe it would be faster if I just wrote all of this myself”.
Her point wasn’t child themes (although I think that’s a perfect thing to point to as the way you work with them is all with overriding what is already there), but the expectation of knowledge:
[…] unless you are “a CSS person” this understanding of specificity and its impact on the future of the code-base is somewhat specialized knowledge. Should everyone who writes CSS be expected to understand these details? Maybe, but the more experienced I become in all kinds of development, I’m starting to think that’s an unrealistic expectation given how much other stuff we have to know as developers.
We all have a number of interests and passions. For example, I’m interested in JavaScript, 90’s indie rock and hip hop, obscure jazz, the city of Pittsburgh, pizza, coffee, and movies starring John Lurie. We also have family members, friends, acquaintances, classmates, and colleagues who also have their own social relationships, interests, and passions. Some of these relationships and interests overlap, like my friend Riley who shares my interest in 90’s hip hop and pizza. Others do not, like my colleague Harrison, who prefers Python to JavaScript, only drinks tea, and prefers current pop music. All together, we each have a connected graph of the people in our lives, and the ways that our relationships and interests overlap.
These types of interconnected data are exactly the challenge that GraphQL initially set out to solve in API development. By writing a GraphQL API we are able to efficiently connect data, which reduces the complexity and number of requests, while allowing us to serve the client precisely the data that it needs. (If you’re into more GraphQL metaphors, check out Meeting GraphQL at a Cocktail Mixer.)
In this article, we’ll build a GraphQL API in Node.js, using the Apollo Server package. To do so, we’ll explore fundamental GraphQL topics, write a GraphQL schema, develop code to resolve our schema functions, and access our API using the GraphQL Playground user interface.
What is GraphQL?
GraphQL is an open source query and data manipulation language for APIs. It was developed with the goal of providing single endpoints for data, allowing applications to request exactly the data that is needed. This has the benefit of not only simplifying our UI code, but also improving performance by limiting the amount of data that needs to be sent over the wire.
What we’re building
To follow along with this tutorial, you’ll need Node v8.x or later and some familiarity with working with the command line.
We’re going to build an API application for book highlights, allowing us to store memorable passages from the things that we read. Users of the API will be able to perform “CRUD” (create, read, update, delete) operations against their highlights:
Create a new highlight
Read an individual highlight as well as a list of highlights
Update a highlight’s content
Delete a highlight
Getting started
To get started, first create a new directory for our project, initialize a new node project, and install the dependencies that we’ll need:
# make the new directory
mkdir highlights-api
# change into the directory
cd highlights-api
# initiate a new node project
npm init -y
# install the project dependencies
npm install apollo-server graphql
# install the development dependencies
npm install nodemon --save-dev
Before moving on, let’s break down our dependencies:
apollo-server is a library that enables us to work with GraphQL within our Node application. We’ll be using it as a standalone library, but the team at Apollo has also created middleware for working with existing Node web applications in Express, hapi, Fastify, and Koa.
graphql includes the GraphQL language and is a required peer dependency of apollo-server.
nodemon is a helpful library that will watch our project for changes and automatically restart our server.
With our packages installed, let’s next create our application’s root file, named index.js. For now, we’ll console.log() a message in this file:
console.log("📚 Hello Highlights");
To make our development process simpler, we’ll update the scripts object within our package.json file to make use of the nodemon package:
"scripts": {
"start": "nodemon index.js"
},
Now, we can start our application by typing npm start in the terminal application. If everything is working properly, you will see 📚 Hello Highlights logged to your terminal.
GraphQL schema types
A schema is a written representation of our data and interactions. By requiring a schema, GraphQL enforces a strict plan for our API. This is because the API can only return data and perform interactions that are defined within the schema. The fundamental component of GraphQL schemas are object types. GraphQL contains five built-in types:
String: A string with UTF-8 character encoding
Boolean: A true or false value
Int: A 32-bit integer
Float: A floating-point value
ID: A unique identifier
We can construct a schema for an API with these basic components. In a file named schema.js, we can import the gql library and prepare the file for our schema syntax:
const { gql } = require('apollo-server');
const typeDefs = gql`
# The schema will go here
`;
module.exports = typeDefs;
To write our schema, we first define the type. Let’s consider how we might define a schema for our highlights application. To begin, we would create a new type with a name of Highlight:
const typeDefs = gql`
type Highlight {
}
`;
Each highlight will have a unique ID, some content, a title, and an author. The Highlight schema will look something like this:
const typeDefs = gql`
type Highlight {
id: ID
content: String
title: String
author: String
}
`;
We can make some of these fields required by adding an exclamation point:
Though we’ve defined an object type for our highlights, we also need to provide a description of how a client will fetch that data. This is called a query. We’ll dive more into queries shortly, but for now let’s describe in our schema the ways in which someone will retrieve highlights. When requesting all of our highlights, the data will be returned as an array (represented as [Highlight]) and when we want to retrieve a single highlight we will need to pass an ID as a parameter.
Now, in the index.js file, we can import our type definitions and set up Apollo Server:
const {ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const server = new ApolloServer({ typeDefs });
server.listen().then(({ url }) => {
console.log(`📚 Highlights server ready at ${url}`);
});
If we’ve kept the node process running, the application will have automatically updated and relaunched, but if not, typing npm start from the project’s directory in the terminal window will start the server. If we look at the terminal, we should see that nodemon is watching our files and the server is running on a local port:
[nodemon] 2.0.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node index.js`
📚 Highlights server ready at http://localhost:4000/
Visiting the URL in the browser will launch the GraphQL Playground application, which provides a user interface for interacting with our API.
GraphQL Resolvers
Though we’ve developed our project with an initial schema and Apollo Server setup, we can’t yet interact with our API. To do so, we’ll introduce resolvers. Resolvers perform exactly the action their name implies; they resolve the data that the API user has requested. We will write these resolvers by first defining them in our schema and then implementing the logic within our JavaScript code. Our API will contain two types of resolvers: queries and mutations.
Let’s first add some data to interact with. In an application, this would typically be data that we’re retrieving and writing to from a database, but for our example let’s use an array of objects. In the index.js file add the following:
let highlights = [
{
id: '1',
content: 'One day I will find the right words, and they will be simple.',
title: 'Dharma Bums',
author: 'Jack Kerouac'
},
{
id: '2',
content: 'In the limits of a situation there is humor, there is grace, and everything else.',
title: 'Arbitrary Stupid Goal',
author: 'Tamara Shopsin'
}
]
Queries
A query requests specific data from an API, in its desired format. The query will then return an object, containing the data that the API user has requested. A query never modifies the data; it only accesses it. We’ve already written a two queries in our schema. The first returns an array of highlights and the second returns a specific highlight. The next step is to write the resolvers that will return the data.
In the index.js file, we can add a resolvers object, which can contain our queries:
The highlights query returns the full array of highlights data. The highlight query accepts two parameters: parent and args. The parent is the first parameter of any GraqhQL query in Apollo Server and provides a way of accessing the context of the query. The args parameter allows us to access the user provided arguments. In this case, users of the API will be supplying an id argument to access a specific highlight.
We can then update our Apollo Server configuration to include the resolvers:
const server = new ApolloServer({ typeDefs, resolvers });
With our query resolvers written and Apollo Server updated, we can now query API using the GraphQL Playground. To access the GraphQL Playground, visit http://localhost:4000 in your web browser.
A query is formatted as so:
query {
queryName {
field
field
}
}
With this in mind, we can write a query that requests the ID, content, title, and author for each our highlights:
query {
highlights {
id
content
title
author
}
}
Let’s say that we had a page in our UI that lists only the titles and authors of our highlighted texts. We wouldn’t need to retrieve the content for each of those highlights. Instead, we could write a query that only requests the data that we need:
query {
highlights {
title
author
}
}
We’ve also written a resolver to query for an individual note by including an ID parameter with our query. We can do so as follows:
query {
highlight(id: "1") {
content
}
}
Mutations
We use a mutation when we want to modify the data in our API. In our highlight example, we will want to write a mutation to create a new highlight, one to update an existing highlight, and a third to delete a highlight. Similar to a query, a mutation is also expected to return a result in the form of an object, typically the end result of the performed action.
The first step to updating anything in GraphQL is to write the schema. We can include mutations in our schema, by adding a mutation type to our schema.js file:
Our newHighlight mutation will take the required value of content along with optional title and author values and return a Highlight. The updateHighlight mutation will require that a highlight id and content be passed as argument values and will return the updated Highlight. Finally, the deleteHighlight mutation will accept an ID argument, and will return the deleted Highlight.
With the schema updated to include mutations, we can now update the resolvers in our index.js file to perform these actions. Each mutation will update our highlights array of data.
With these mutations written, we can use the GraphQL Playground to practice mutating the data. The structure of a mutation is nearly identical to that of a query, specifying the name of the mutation, passing the argument values, and requesting specific data in return. Let’s start by adding a new highlight:
mutation {
newHighlight(author: "Adam Scott" title: "JS Everywhere" content: "GraphQL is awesome") {
id
author
title
content
}
}
We can then write mutations to update a highlight:
mutation {
updateHighlight(id: "3" content: "GraphQL is rad") {
id
content
}
}
And to delete a highlight:
mutation {
deleteHighlight(id: "3") {
id
}
}
Wrapping up
Congratulations! You’ve now successfully built a GraphQL API, using Apollo Server, and can run GraphQL queries and mutations against an in-memory data object. We’ve established a solid foundation for exploring the world of GraphQL API development.
In this post, Sarah Chima walks us through how we can work with browser events, such as clicking, using JavaScript. There’s a ton of great info in here! If JavaScript isn’t your strong suit, I think this is the best explanation of event handling that I’ve read in quite some time.
When an event occurs, the browser creates an event object and passes it as an argument to the event handler. This event object contains details of the event. For instance, you might want to know which button was clicked or which key was pressed or the mouse coordinates when the event occurred. You can get all of these from the event object.
Sarah also links to this really good primer on bubbling and capturing that’s worth checking out, too.
Brent Jackson says CSS utility libraries failed somewhat:
Eventually, you’ll need to add one-off styles that just aren’t covered by the library you’re using, and there isn’t always a clear way to extend what you’re working with. Without a clear way to handle things like this, developers tend to add inconsistent hacks and append-only styles.
I have a feeling Tailwind people would disagree. I have no particular opinion here, I’m just noting that Tailwind seems to have a more fervent fanbase than those early days of Basscss/Tachyons.
Brent goes on to say that CSS-in-JS solves the same problem, but in a better way:
CSS-in-JS libraries help solve a lot of the same issues Utility-based CSS methodologies were focused on (and more) in a much better way. They connect styles directly to elements without needing to name things or create abstractions in class selectors. They avoid append-only stylesheets with encapsulation and hashed classnames. These libraries work with existing build tools, allowing for code splitting, lazy loading, and dead code elimination with virtually zero effort, and they don’t require additional tools like Sass or PostCSS. Many libraries also include CSS performance optimizations, such as critical CSS, enabled by default so that developers don’t need additional tooling or even need to think about them.
No wonder people have been raving about this.
The one-step-back refers to the fact that CSS-in-JS is more open-ended and doesn’t encourage consistency as much. I’m not sure about that. Seems like if you’re building in a component-based way already, consistency kind of comes along for the ride, even before using design tokens and the like — which a CSS-in-JS approach also encourages.
You might reach for <input type="number> when you’re, you know, trying to collect a number in a form. But it’s got all sorts of issues. For one, sometimes what you want kinda looks like a number, but isn’t one (like how a credit card number has spaces), because it’s really just a string of numbers. Even more importantly, there are a variety of screen reader problems.
That last attribute is interesting and new to me. It means you get this super extra useful experience on browsers that support it:
There are other autocomplete values, as Phil writes:
There are many autocomplete values available, covering everything from names and addresses to credit cards and other account details. For sign up and login there are a few autocomplete values that stand out as useful hints: username, email, new-password, current-password.
Browsers and password managers have very good heuristics to find login forms on web pages, but using the username and current-password values make it very obvious.