Earlier this week, Bastian Allgeier published some interesting thoughts about complexity in web development and how changing simple things can often feel far more difficult than they need to be:
You want to build a JS file? Please update Webpack first. Oh, that new version of Webpack is no longer compatible with your Node version. Oh, your new Node version is no longer compatible with that other dependency. Oh, now you have 233 detected security issues in all your node_modules but you can’t fix them because that would break something completely unrelated.
It’s a UX nightmare and I haven’t found a single exception yet. Vue Cli or Parcel are the most positive examples, where positive means: not as horrible as the rest.
This dependency hell is also the reason why old projects are almost like sealed capsules. You can hardly let a project lie around for more than a year, because afterwards it’s probably broken.
A couple of weeks ago, I returned to a web app that was built with a variety of tools I hadn’t updated in quite some time and realized that it would be an enormous effort to fix all the packages and dependencies; instead I should just start over again. I can certainly empathise with Bastian on this stuff.
This reminds me that Chris wrote a great essay not so long about simple web development and collected a ton of thoughts from other developers out there.
A little while back, you might have bumped into this CSS-Tricks article where I described how a mixin can be used to automate responsive font sizes using RFS. In its latest version, v9, RFS is capable of rescaling any value for value for any CSS property with px or rem units, like margin, padding, border-radius or even box-shadow.
Hey, Marc here from Frontend Masters — excited to support CSS-Tricks ❤️!
Have you checked out Sarah Drasner’s courses yet? She has two awesome courses on Design for Developers and SVG! Plus another introducing Vue.js!
Design for Developers
In the Design for Developers course, you’ll learn to become self-sufficient throughout the entire lifecycle of the project — from concept to design to implementation!
You’ll learn to…
Code dynamic layouts and understand the principles of composition.
Select the typography that compliments your design by following simple rules.
Choose colors palettes, and understand the theories to understand why they work together.
Know how and when to reach for design tools such as Photoshop or Sketch.
Use the correct image formats for performance.
Prototype to quickly communicate your ideas and get your layout up-and-running.
SVG Essentials and Animation
In the SVG Essentials and Animation course, you’ll learn to build and optimize SVG – the scalable graphics format for the web that can achieve impressively small file sizes for fast-loading websites.
You’ll learn to…
〰 Create platonic and custom shapes with path commands.
⚡️ Optimize SVG to achieve smaller file sizes for performance.
💻 Assemble new SVGs with code and graphics programs.
🖱 Control complex animations and timelines with user input.
💥 Leverage GreenSock’s JavaScript libraries for immersive animation effects.
Introduction to Vue.js
In the Introduction to Vue.js course, you’ll get started quickly with the Vue.js JavaScript framework!
You’ll learn to…
Build custom, reusable components and animate them.
Use props, slots, and scoped styles to create flexible components.
Grok advanced features like filters and mixins for transforming data.
Get a single page application up and running fast with the Vue-CLI.
Work with Vuex to manage the state of larger-scale applications.
This course is for developers with an intermediate knowledge of JavaScript who want to learn how to build and maintain complex applications quickly and efficiently.
I’ve recently noticed an interesting change on CodePen: on hovering the pens on the homepage, there’s a rectangle with rounded corners expanding in the back.
Expanding box effect on the CodePen homepage.
Being the curious creature that I am, I had to check how this works! Turns out, the rectangle in the back is an absolutely positioned ::after pseudo-element.
Initial ::after styles. A positive offset goes inwards from the parent’s padding limit, while a negative one goes outwards.
On :hover, its offsets are overridden and, combined with the transition, we get the expanding box effect.
The ::after styles on :hover.
The right property has the same value (-1rem) in both the initial and the :hover rule sets, so it’s unnecessary to override it, but all the other offsets move by 2rem outwards (from 1rem to -1rem for the top and left offsets and from -1rem to -3rem for the bottom offset)
One thing to notice here is that the ::after pseudo-element has a border-radius of 10px which gets preserved as it expands. Which got me to think about what methods we have for expanding/shrinking (pseudo-) elements while preserving their border-radius. How many can you think of? Let me know if you have ideas that haven’t been included below, where we take a look at a bunch of options and see which is best suited for what situation.
Changing offsets
This is the method used on CodePen and it works really well in this particular situation for a bunch of reasons. First off, it has great support. It also works when the expanding (pseudo-) element is responsive, with no fixed dimensions and, at the same time, the amount by which it expands is fixed (a rem value). It also works for expanding in more than two directions (top, bottom and left in this particular case).
There are however a couple of caveats we need to be aware of.
First, our expanding element cannot have position: static. This is not a problem in the context of the CodePen use case since the ::after pseudo-element needs to be absolutely positioned anyway in order to be placed underneath the rest of this parent’s content.
Second, going overboard with offset animations (as well as, in general, animating any property that affects layout with box properties the way offsets, margins, border widths, paddings or dimensions do) can negatively impact performance. Again, this is not something of concern here, we only have a little transition on :hover, no big deal.
Changing dimensions
Instead of changing offsets, we could change dimensions instead. However, this is a method that works if we want our (pseudo-) element to expand in, at most, two directions. Otherwise, we need to change offsets as well. In order to better understand this, let’s consider the CodePen situation where we want our ::after pseudo-elements to expand in three directions (top, bottom and left).
The relevant initial sizing info is the following:
Since opposing offsets (the top–bottom and left–right pairs) cancel each other (1rem - 1rem = 0), it results that the pseudo-element’s dimensions are equal to those of its parent (or 100% of the parent’s dimensions).
…is not enough, as this makes the height increase the downward direction by 4rem instead of increasing it by 2rem up and 2rem down. The following demo illustrates this (put :focus on or hover over the items to see how the ::after pseudo-element expands):
But, to be honest, this feels less desirable than changing offsets alone.
However, changing dimensions is a good solution in a different kind of situation, like when we want to have some bars with rounded corners that expand/shrink in a single direction.
Note that, if we didn’t have rounded corners to preserve, the better solution would be to use directional scaling via the transform property.
Changing padding/border-width
Similar to changing the dimensions, we can change the padding or border-width (for a border that’s transparent). Note that, just like with changing the dimensions, we need to also update offsets if expanding the box in more than two dimensions:
In the demo above, the pinkish box represents the content-box of the ::after pseudo-element and you can see it stays the same size, which is important for this approach.
In order to understand why it is important, consider this other limitation: we also need to have the box dimensions defined by two offsets plus the width and the height instead of using all four offsets. This is because the padding/ border-width would only grow inwards if we were to use four offsets rather than two plus the width and the height.
In spite of these limitations, this method can come in handy if our expanding (pseudo-) element has text content we don’t want to see moving around on :hover as illustrated by the Pen below, where the first two examples change offsets/ dimensions, while the last two change paddings/ border widths:
This is another approach that works great for the CodePen situation, though I cannot really think of other use cases. Also note that, just like changing offsets or dimensions, this method affects the size of the content-box, so any text content we may have gets moved and rearranged.
Changing font size
This is probably the trickiest one of all and has lots of limitations, the most important of which being we cannot have text content on the actual (pseudo-) element that expands/shrinks — but it’s another method that would work well in the CodePen case.
Also, font-size on its own doesn’t really do anything to make a box expand or shrink. We need to combine it with one of the previously discussed properties.
For example, we can set the font-size on ::after to be equal to 1rem, set the offsets to the expanded case and set em margins that would correspond to the difference between the expanded and the initial state.
If you’ve read pieces on animation performance, then you’ve probably read it’s better to animate transforms instead of properties that impact layout, like offsets, margins, borders, paddings, dimensions — pretty much what we’ve used so far!
The first issue that stands out here is that scaling an element also scales its corner rounding, as illustrated below:
We can get around this by also scaling the border-radius the other way.
Let’s say we scale an element by a factor $fx along the x axis and by a factor $fy along the y axis and we want to keep its border-radius at a constant value $r.
This means we also need to divide $r by the corresponding scaling factor along each axis.
However, note that with this method, we need to use scaling factors, not amounts by which we expand our (pseudo-) element in this or that direction. Getting the scaling factors from the dimensions and expansion amounts is possible, but only if they’re expressed in units that have a certain fixed relation between them. While preprocessors can mix units like in or px due to the fact that 1in is always 96px, they cannot resolve how much 1em or 1% or 1vmin or 1ch is in px as they lack context. And calc() is not a solution either, as it doesn’t allow us to divide a length value by another length value to get a unitless scale factor.
This is why scaling is not a solution in the CodePen case, where the ::after boxes have dimensions that depend on the viewport and, at the same time, expand by fixed rem amounts.
But if our scale amount is given or we can easily compute it, this is an option to consider, especially since making the scaling factors custom properties we then animate with a bit of Houdini magic can greatly simplify our code.
This is a method I really like, even though it cuts out pre-Chromium Edge and Internet Explorer support.
Pretty much every usage example of clip-path out there has either a polygon() value or an SVG reference value. However, if you’ve seen some of my previous articles, then you probably know there are other basic shapes we can use, like inset(), which works as illustrated below:
So, in order to reproduce the CodePen effect with this method, we set the ::after offsets to the expanded state values and then cut out what we don’t want to see with the help of clip-path:
Furthermore, it doesn’t really break anything in non-supporting browsers, it just always stays in the expanded state.
However, while this is method that works great for a lot of situations — including the CodePen use case — it doesn’t work when our expanding/shrinking elements have descendants that go outside their clipped parent’s border-box, as it is the case for the last example given with the previously discussed scaling method.
In this week’s roundup, a handy bookmarklet for inspecting typography, using await to tinker with how JavaScript modules import one another, plus Facebook’s in-app browser is only posing as one. Let’s get into the news!
Check if your content breaks after increasing text spacing
Dylan Barrell from Deque has created a bookmarklet that you can use to check if there are any issues with the content or functionality of your website after increasing the line, paragraph, letter, and word spacing, according to the “Text Spacing” success criterion of the Web Content Accessibility Guidelines.
The proposed top-level await feature is especially useful in JavaScript modules: If module A uses top-level await (e.g., to connect to a database), and module B imports module A — via the import declaration — then the body of B will be evaluated after the body of A (i.e., B will correctly wait for A).
Top-level await enables modules to act as big async functions: With top-level await, ECMAScript Modules (ESM) can await resources, causing other modules who import them to wait before they start evaluating their body.
AMP has created a new multi-stage loading indicator that has better perceived performance (tested on 2,500 users): It shows nothing until 0.5s, then an intermediate animation until 3.5s, and finally a looping spinner after that.
AMP has released the <amp-script> element which, for the first time, allows AMP pages to add custom JavaScript, with some constraints: The code runs in a separate worker thread and requires a user gesture to change page content (via AMP Project).
The HTML Standard has made autofocus a global attribute that “applies to all elements, not just to form controls” (e.g., this change enables <div contenteditable autofocus>, but no browser supports this yet) (via Kent Tamura).
Facebook’s in-app browser (powered by Android’s WebView) is not a browser: “Facebook is breaking the web for 20–30% of your traffic because you aren’t demanding they do better” (via Alex Russell).
Read more news in my new, weekly Sunday issue. Visit webplatform.news for more information.
You’ve created a Progressive Web App (PWA), designed an icon to represent it, and now you’re installing it to your Android home screen.
But, if you have a recent Android phone, your icons will show up like this:
What happened? Well, Android Oreo introduced adaptive icons, a new icon format that enforces the same shape for all icons on the home screen. Icons that don’t follow the new format are given a white background.
However, there is a new web feature called maskable icons that is coming soon to Firefox Preview and other web browsers. This new icon format will let your PWAs have their own adaptive icons on Android.
I work at Mozilla and have implemented support for maskable icons in Firefox Preview. I’ll show you how to add them to your own PWAs for Android.
What are maskable and adaptive icons?
Until a few years ago, Android app icons were freeform and could be any shape. This meant that web apps could also reuse the same transparent icon when pinned to the home screen.
However, manufacturers, like Samsung, wanted to make all icons on a device the same shape to keep things consistent. Some manufacturers even wanted different shapes. To deal with the variety of requirements from manufacturers and devices, Android introduced “adaptive icons.” You supply an image with extra space around the edges, and Android will crop it to the correct shape.
But web apps are designed to work on any platform, so they don’t have APIs to create these special Android icons. Instead, icons would get squished into white boxes like this:
Lo and behold, last September a brand new API descended upon us and was added to the W3C spec. Maskable icons allow web developers to specify a full-bleed icon that will be cropped. It’s platform agnostic, so Windows could use them for tiles or iOS could use them for icons.
How to create maskable icons
Since the maskable icon format is designed work with any platform, the size and ratios are different from the size and ratios of Android’s adaptive icons. This means you can’t reuse the same asset.
Maskable icons can be any size, and you can continue to use the same sizes that you’d use for normal transparent icons. But when designing the icon, ensure that important information is within a “safe zone” circle with a radius equal to 40% of the image’s size.
The safe zone
All pixels within this zone are guaranteed to be visible. Pixels outside the zone may be cropped off depending on the icon shape and the platform.
Warning: If you already have an Android app, avoid copying and pasting the icon from your Android app to your web app. The ratios are different, so your icons would look too small.
Adding the icon to your Web App Manifest
Once the icons are created, you can add an entry to your Web App Manifest similar to other icon assets. The Web App Manifest provides information about your web app in a JSON file, and includes an "icons" array.
Maskable icons use a special new key, "purpose", to indicate that they are meant to be used with icon masks. Icons with transparent backgrounds have a default "purpose" of "any", and icons can be used for multiple purposes by separating each option with a space.
"purpose": "maskable any"
Preview your icons
Do you want to see what your own maskable icons will look like? I’ve created a tool, Maskable.app, to help you evaluate how the icon appears in different shapes.
The app lets you preview your icon in various shapes that can be found on Android devices. I hope this tool helps you create unique icons for your Progressive Web Apps.
Once you’re satisfied with the results, you can start testing your app with Mozilla’s Reference Browser. This special browser is a testing ground for features before they reach Firefox Preview, and you can use it to check out how your PWA looks. Chrome is working on maskable icon support too.
Tools like PWACompat also have support for maskable icons. You can automatically generate icons for iOS and other devices based on your new maskable icons!
Time to build your own icons
If you want to more control over how your PWA icons are displayed on Android, maskable icons is the way to go. With maskable icons you can customize how your icon is displayed from edge-to-edge. Hopefully this article can get you started on creating your first maskable icon.
Before we come to how to style underlines, we should answer the question: should we underline?
In graphic design, underlines are generally seen as unsophisticated. There are nicer ways to draw emphasis, to establish hierarchy, and to demarcate titles.
If you feel the urge to underline, use bold or italic instead. In special situations, like headings, you can also consider using all caps, small caps, or changing the point size. Not convinced? I invite you to find a book, newspaper, or magazine that underlines text. That look is mostly associated with supermarket tabloids.
But the web is different. Hyperlinks are the defining feature of the internet; and from the internet’s inception, they have been underlined. It’s a universally understood convention. The meaning is crystal clear — an underline means a link.
However, plenty of popular websites have ditched underlines: The New York Times, New YorkMagazine, The Washington Post, Bloomberg, Amazon, Apple, GitHub, Twitter, Wikipedia. When they removed underlines from their search results page in 2014, Google lead designer Jon Wiley argued that it created a cleaner look. Notably though, the majority of these sites have kept slight variances on the traditional lurid blue color (#0000EE) that’s been the browser default since the beginning of the web. While this provides a visual cue for the majority of users, it may not be enough to pass WCAG accessibility compliance.
Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.
— WCAG 2.1
WCAG do not strictly mandate using underlines for links, but it does recommend them. Color blind users need to be able to discern a link. You could differentiate them in other ways, such as with a bold font-weight. Or you could keep this long-established visual affordance. But if we’re going to use underlines, we want them to look nice. Marcin Wichary, a designer at Medium, described the perfect underline as:
[…] visible, but unobtrusive — allowing people to realize what’s clickable, but without drawing too much attention to itself. It should be positioned at just the right distance from the text, sitting comfortably behind it for when descenders want to occupy the same space.
Achieving this has traditionally required CSS tricks.
The hacks we’ve had
This is one trick all developers will be familiar with: border-bottom. By emulating an underline using border-bottom, we gain control over color and thickness. These pseudo-underlines have one problem: an overly large distance from the text. They are underneath the descenders of the letters. You could potentially solve this issue by using line-height, but that comes with its own issues. A similar technique utilises box-shadow. Marcin Wichary pioneered the most sophisticated technique, using background-image to simulate an underline. They were useful hacks but are thankfully no longer needed.
Styling real underlines
Finally we can demarcate links without sacrificing style thanks to two new CSS properties.
text-underline-offset controls the position of the underline.
text-decoration-thickness controls the thickness of underlines, as well as overlines, and line-throughs.
You can also specify from-font to both of these properties which will pull the relevant metric from the used font file itself.
UX agency Clearleft make bold use of (pseudo) underlines, calling clear attention to links with colorful styling. Here’s one example of a faux underline:
a {
text-decoration: none;
border-bottom: #EA215A 0.125em solid;
}
Notice that this fake underline is clearly below the descender of the letter „y”:
Here’s the same paragraph, using DevTools to apply the same styling to a real underline using the new CSS properties:
a {
text-decoration-color: #EA215A;
text-decoration-thickness: .125em;
text-underline-offset: 1.5px;
}
You’ll notice I’m using the em unit in my example code. The spec strongly encourages using it rather than pixels so that the thickness scales with the font.
These properties have already shipped in Safari and are coming in Firefox 70.
With the move to Chromium for Microsoft’s Edge browser, we will finally have cross browser support for the text-decoration-style property, which offers the options: solid (the default), double, dotted, dashed, and wavy. When combined, these new properties open up a whole range of possibilities.
Perhaps the biggest upgrade for underlines on the web, however, has come without developers needing to do anything. In the bad old days, descenders were unceremoniously sliced through by underlines, which was far from elegant. Developers used to hack around this shortcoming by applying a text-shadow that matched the background color. text-decoration-skip-ink brought a better way to make space for descenders.
The default value of auto (left) and a value of none (right)
Handily, it’s set as the new default value for underlines; meaning the look of underlines has improved while most web developers remain unaware that this property exists. Should you want an underline to cross over glyphs, you can set this property to none.
Before we come to how to style underlines, we should answer the question: should we underline?
In graphic design, underlines are generally seen as unsophisticated. There are nicer ways to draw emphasis, to establish hierarchy, and to demarcate titles.
If you feel the urge to underline, use bold or italic instead. In special situations, like headings, you can also consider using all caps, small caps, or changing the point size. Not convinced? I invite you to find a book, newspaper, or magazine that underlines text. That look is mostly associated with supermarket tabloids.
But the web is different. Hyperlinks are the defining feature of the internet; and from the internet’s inception, they have been underlined. It’s a universally understood convention. The meaning is crystal clear — an underline means a link.
However, plenty of popular websites have ditched underlines: The New York Times, New YorkMagazine, The Washington Post, Bloomberg, Amazon, Apple, GitHub, Twitter, Wikipedia. When they removed underlines from their search results page in 2014, Google lead designer Jon Wiley argued that it created a cleaner look. Notably though, the majority of these sites have kept slight variances on the traditional lurid blue color (#0000EE) that’s been the browser default since the beginning of the web. While this provides a visual cue for the majority of users, it may not be enough to pass WCAG accessibility compliance.
Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.
— WCAG 2.1
WCAG do not strictly mandate using underlines for links, but it does recommend them. Color blind users need to be able to discern a link. You could differentiate them in other ways, such as with a bold font-weight. Or you could keep this long-established visual affordance. But if we’re going to use underlines, we want them to look nice. Marcin Wichary, a designer at Medium, described the perfect underline as:
[…] visible, but unobtrusive — allowing people to realize what’s clickable, but without drawing too much attention to itself. It should be positioned at just the right distance from the text, sitting comfortably behind it for when descenders want to occupy the same space.
Achieving this has traditionally required CSS tricks.
The hacks we’ve had
This is one trick all developers will be familiar with: border-bottom. By emulating an underline using border-bottom, we gain control over color and thickness. These pseudo-underlines have one problem: an overly large distance from the text. They are underneath the descenders of the letters. You could potentially solve this issue by using line-height, but that comes with its own issues. A similar technique utilises box-shadow. Marcin Wichary pioneered the most sophisticated technique, using background-image to simulate an underline. They were useful hacks but are thankfully no longer needed.
Styling real underlines
Finally we can demarcate links without sacrificing style thanks to two new CSS properties.
text-underline-offset controls the position of the underline.
text-decoration-thickness controls the thickness of underlines, as well as overlines, and line-throughs.
You can also specify from-font to both of these properties which will pull the relevant metric from the used font file itself.
UX agency Clearleft make bold use of (pseudo) underlines, calling clear attention to links with colorful styling. Here’s one example of a faux underline:
a {
text-decoration: none;
border-bottom: #EA215A 0.125em solid;
}
Notice that this fake underline is clearly below the descender of the letter „y”:
Here’s the same paragraph, using DevTools to apply the same styling to a real underline using the new CSS properties:
a {
text-decoration-color: #EA215A;
text-decoration-thickness: .125em;
text-underline-offset: 1.5px;
}
You’ll notice I’m using the em unit in my example code. The spec strongly encourages using it rather than pixels so that the thickness scales with the font.
These properties have already shipped in Safari and are coming in Firefox 70.
With the move to Chromium for Microsoft’s Edge browser, we will finally have cross browser support for the text-decoration-style property, which offers the options: solid (the default), double, dotted, dashed, and wavy. When combined, these new properties open up a whole range of possibilities.
Perhaps the biggest upgrade for underlines on the web, however, has come without developers needing to do anything. In the bad old days, descenders were unceremoniously sliced through by underlines, which was far from elegant. Developers used to hack around this shortcoming by applying a text-shadow that matched the background color. text-decoration-skip-ink brought a better way to make space for descenders.
The default value of auto (left) and a value of none (right)
Handily, it’s set as the new default value for underlines; meaning the look of underlines has improved while most web developers remain unaware that this property exists. Should you want an underline to cross over glyphs, you can set this property to none.
The DOM is just a little weird about some things, and the way you deal with attributes is no exception. There are a number of ways to deal with the attributes on elements. By attributes, I mean things like the id in <div id="cool"></div>. Sometimes you need to set them. Sometimes you need to get them. Sometimes you get fancy helpers. Sometimes you don’t.
For this article, I’ll assume el is a DOM element in your JavaScript. Let’s say you’ve done something like const el = document.querySelector("#cool"); and matched <div id="cool"> or whatever.
Some attributes are also attributes of the DOM object itself, so iff you need to set an id or title, you can do:
el.id; // "cool"
el.title = "my title";
el.title; // "my title";
Others that work like that are lang, align, and all the big events, like onclick.
Then there are attributes that work similarly to that but are nested deeper. The style attribute is like that. If you log el.style you’ll see a ton of CSS style declarations. You can get and set them easily:
You can get computed colors this way too. If you do module.style.color hoping to get the color of an element out of the gate, you probably won’t get it. For that, you’d have to do:
let style = window.getComputedStyle(el);
style.color; // whatever in CSS won out
But not all attributes are like first-class attributes like that.
el['aria-hidden'] = true; // nope
That „works” in that it sets that as a property, but it doesn’t set it in the DOM the proper way. Instead, you’ll have to use the generic setter and getter functions that work for all attributes, like:
el.classList.value; // "module big"
el.classList.length; // 2
el.classList.add("cool"); // adds the class "cool", so "module big cool"
el.classList.remove("big"); // removes "big", so "module cool"
el.classList.toggle("big"); // adds "big" back, because it was missing (goes back and forth)
el.classList.contains("module"); // true
There’s even more, and classList itself behaves like an array so you can forEach it and such. That’s a pretty strong reason to use classes, as the DOM API around them is so handy.
Another attribute type that has a somewhat fancy help is data-*. Say you’ve got:
Listen, I am no GraphQL expert but I do enjoy working with it. The way it exposes data to me as a front-end developer is pretty cool. It’s like a menu of available data and I can ask for whatever I want. That’s a massive improvement over REST and highly empowering for me as a front-end developer who desires to craft components with whatever data I think is best for a UI without having to make slew of calls for data or ask a back-end developer to help make me a new bespoke API for my new needs.
But… who builds that menu of data? Somebody does.
If that somebody is a person or team at your own company because you’ve built out your own GraphQL API for your own needs, that’s great. Now you’ve got control over what goes in there (and when and how).
But sometimes GraphQL APIs are just handed to you. Perhaps that is how your CMS delivers its data. Still cool and useful, but that control is at the mercy of the CMS. You still have a menu of options, but the menu just is what it is. No substitutes, to continue the metaphor. If the menu doesn’t have what you need, you can’t go back into the kitchen and add extra sauerkraut to that reuben or have the steak fries come with fried mushrooms.
This came up in a discussion with Simen Skogsrud and Knut Melvær on an episode of ShopTalk. Their product, Sanity, is like cloud storage for JSON data, and a CMS if you need it. A modern product like this, you’d think a GraphQL API would be a no-brainer, and indeed, they have a beta for it.
But instead of GraphQL being the main first-class citizen way of querying for and mutating data, they have their own special language: GROQ. At first glance, I’m like: eeeeeesh, there’s a way to shoot yourself in the foot. Invent some special language that people have to learn that’s unique to your product instead of the emerging industry standard.
But Simen and Knut made a good point about some of the limitations of GraphQL in the context of a third-party handing you an API: you get what you get. Say a GraphQL API offers a way to retrieve authors. A generic API for that is probably designed something like this:
But what I actually want is just how many authors we have on the site. Perhaps I wish I could do this:
{
allAuthors {
count
}
}
But that’s not in the API I was given. Well, too bad. I guess I’ll have to request all the authors and count them myself. I might not control the API.
This means that something like a CMS that offers a GraphQL endpoint needs to make a choice. They are either very strict and you just get-what-you-get. Or, they offer not only a GraphQL API but a way to control and augment what goes into that API.
In Santiy’s case, rather than offer the later, they offer GROQ, which is a query language that is powerful enough you can get whatever you want out of the (JSON) data. And rather than making it this proprietary Sanity-only thing, they’ve open sourced it.
With GROQ, I don’t need any permission or alterations to the API to ask how many authors there are. I’d do something like…
{ "totalAuthors": count(*[* in authors]) }
(I actually have no idea if the above code is accurate, and of course, it depends on the JSON it is querying, but it’s just conceptual anyway.)
By giving someone a query language that is capable of selecting any possible data in the data store, it has a big benefit:
You can query for literally anything
You don’t need a middle layer of configuration
But it comes at a cost:
Complexity of syntax
No middle layer means less opportunity for connecting multiple APIs, exposing only certain data based on permissions, etc.