Tag: Cool

Nailing That Cool Dissolve Transition

We’re going to create an impressive transition effect between images that’s, dare I say, very simple to implement and apply to any site. We’ll be using the kampos library because it’s very good at doing exactly what we need. We’ll also explore a few possible ways to tweak the result so that you can make it unique for your needs and adjust it to the experience and impression you’re creating.

Take one look at the Awwwards Transitions collection and you’ll get a sense of how popular it is to do immersive effects, like turning one media item into another. Many of those examples use WebGL for the job. Another thing they have in common is the use of texture mapping for either a displacement or dissolve effect (or both).

To make these effects, you need the two media sources you want to transition from and to, plus one more that is the map, or a grid of values for each pixel, that determines when and how much the media flips from one image to the next. That map can be a ready-made image, or a <canvas> that’s drawn upon, say, noise. Using a dissolve transition effect by applying a noise as a map is definitely one of those things that can boost that immersive web experience. That’s what we’re after.

Setting up the scene

Before we can get to the heavy machinery, we need a simple DOM scene. Two images (or videos, if you prefer), and the minimum amount of JavaScript to make sure they’re loaded and ready for manipulation.

<main>   <section>     <figure>       <canvas id="target">         <img id="source-from" src="path/to/first.jpg" alt="My first image" />         <img id="source-to" data-src="path/to/second.jpg" alt="My second image" />       </canvas>     <figure>   </section> </main>

This will give us some minimal DOM to work with and display our scene. The stage is ready; now let’s invite in our main actors, the two images:

// Notify when our images are ready function loadImage (src) {   return new Promise(resolve => {     const img = new Image();     img.onload = function () {       resolve(this);     };     img.src = src;   }); } // Get the image URLs const imageFromSrc = document.querySelector('#source-from').src; const imageToSrc = document.querySelector('#source-to').dataset.src; // Load images  and keep their promises so we know when to start const promisedImages = [   loadImage(imageFromSrc),   loadImage(imageToSrc) ];

Creating the dissolve map

The scene is set, the images are fetched — let’s make some magic! We’ll start by creating the effects we need. First, we create the dissolve map by creating some noise. We’ll use a Classic Perlin noise inside a turbulence effect which kind of stacks noise in different scales, one on top of the other, and renders it onto a <canvas> in grayscale:

const turbulence = kampos.effects.turbulence({ noise: kampos.noise.perlinNoise });

This effect kind of works like the SVG feTurbulence filter effect. There are some good examples of this in “Creating Patterns With SVG Filters” from Bence Szabó.

Second, we set the initial parameters of the turbulence effect. These can be tweaked later for getting the specific desired visuals we might need per case:

// Depending of course on the size of the target canvas const WIDTH = 854; const HEIGHT = 480; const CELL_FACTOR = 2; const AMPLITUDE = CELL_FACTOR / WIDTH;  turbulence.frequency = {x: AMPLITUDE, y: AMPLITUDE}; turbulence.octaves = 1; turbulence.isFractal = true;

This code gives us a nice liquid-like, or blobby, noise texture. The resulting transition looks like the first image is sinking into the second image. The CELL_FACTOR value can be increased to create a more dense texture with smaller blobs, while the octaves=1 is what’s keeping the noise blobby. Notice we also normalize the amplitude to at least the larger side of the media, so that texture is stretched nicely across our image.

Next we render the dissolve map. In order to be able to see what we got, we’ll use the canvas that’s already in the DOM, just for now:

const mapTarget = document.querySelector('#target'); // instead of document.createElement('canvas'); mapTarget.width = WIDTH; mapTarget.height = HEIGHT;  const dissolveMap = new kampos.Kampos({   target: mapTarget,   effects: [turbulence],   noSource: true }); dissolveMap.draw(); 

Intermission

We are going to pause here and examine how changing the parameters above affects the visual results. Now, let’s tweak some of the noise configurations to get something that’s more smoke-like, rather than liquid-like, say:

const CELL_FACTOR = 4; // instead of 2

And also this:

turbulence.octaves = 8; // instead of 1

Now we have a more a dense pattern with eight levels (instead of one) superimposed, giving much more detail:

Fantastic! Now back to the original values, and onto our main feature…

Creating the transition

It’s time to create the transition effect:

const dissolve = kampos.transitions.dissolve(); dissolve.map = mapTarget; dissolve.high = 0.03; // for liquid-like effect

Notice the above value for high? This is important for getting that liquid-like results. The transition uses a step function to determine whether to show the first or second media. During that step, the transition is done smoothly so that we get soft edges rather than jagged ones. However, we keep the low edge of the step at 0.0 (the default). You can imagine a transition from 0.0 to 0.03 is very abrupt, resulting in a rapid change from one media to the next. Think of it as clipping.

On the other hand, if the range was 0.0 to 0.5, we’d get a wider range of “transparency,” or a mix of the two images — like we would get with partial opacity — and we’ll get a smoke-like or “cloudy” effect. We’ll try that one in just a moment.

Before we continue, we must remember to replace the canvas we got from the document with a new one we create off the DOM, like so:

const mapTarget = document.createElement('canvas');

Plug it in, and… action!

We’re almost there! Let’s create our compositor instance:

const target = document.querySelector(‘#target’); const hippo = new kampos.Kampos({target, effects: [dissolve]});

And finally, get the images and play the transition:

Promise.all(promisedImages).then(([fromImage, toImage]) => {   hippo.setSource({media: fromImage, width, height});   dissolve.to = toImage;   hippo.play(time => {     // a sin() to play in a loop     dissolve.progress = Math.abs(Math.sin(time * 4e-4)); // multiply time by a factor to slow it down a bit   }); });

Sweet!

Special effects

OK, we got that blobby goodness. We can try playing a bit with the parameters to get a whole different result. For example, maybe something more smoke-like:

const CELL_FACTOR = 4; turbulence.octaves = 8;

And for a smoother transition, we’ll raise the high edge of the transition’s step function:

dissolve.high = 0.3;

Now we have this:

Extra special effects

And, for our last plot twist, let’s also animate the noise itself! First, we need to make sure kampos will update the dissolve map texture on every frame, which is something it doesn’t do by default:

dissolve.textures[1].update = true;

Then, on each frame, we want to advance the turbulence time property, and redraw it. We’ll also slow down the transition so we can see the noise changing while the transition takes place:

hippo.play(time => {   turbulence.time = time * 2;   dissolveMap.draw();   // Notice that the time factor is smaller here   dissolve.progress = Math.abs(Math.sin(time * 2e-4)); });

And we get this:

That’s it!

Exit… stage right

This is just one example of what we can do with kampos for media transitions. It’s up to you now to mix the ingredients to get the most mileage out of it. Here are some ideas to get you going:

  • Transition between site/section backgrounds
  • Transition between backgrounds in an image carousel
  • Change background in reaction to either a click or hover
  • Remove a custom poster image from a video when it starts playing

Whatever you do, be sure to give us a shout about it in the comments.


The post Nailing That Cool Dissolve Transition appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,

:where() has a cool specificity trick, too.

There is a lot of hype on the :is() pseudo-selector lately, probably because now that Safari 14 has it, it’s supported across all the major browsers. You’ve got Miriam tweeting about it, Kevin Powell doing a video, Šime getting it into the Web Platform News, and Robin mentioning it. Bramus really puts a point on it with these “three important facts”:

1. The selector list of :is() is forgiving
2. The specificity of :is() is that of its most specific argument
3. :is() does not work with pseudo-element selectors (for now)

Plus, of course, it’s main functionality which is making otherwise rather verbose, complex, and error-prone selectors easier to write. The specificity thing is extra-interesting. Miriam notes some trickery you could do with it, like juicing up specificity without actually selecting anything.

Say you wanted to use the .button class to select, but give it a ton of specificity

:is(.button, #increase#specificity) {   /* specificity is now (0, 1, 0, 0) instead of (0, 0, 1, 0) }

I’ve done silly stuff like this in the past:

.button.button.button {   /* forcing the selector to be (0, 0, 3, 0) instead of (0, 0, 1, 0) */   /* doesn't actually require element to have three button classes lol */ }

The :is() trick seems a little more understandable to me.

But what if you want to go the other way with specificity and lower it instead? Well, that’s the whole point of the :where() pseudo-selector. Functionally, it’s exactly the same as :is(). You give it a comma-separated list of things to select as part of the selector chain, and it does, with the same forgiving nature. Except, the specificity of the entire :where() part is zero (0).

Kevin showed off an interesting gotcha with :is() in the video:

.card :is(.title, p) {   color: red; }  .card p {   color: yellow; }

You might think yellow will win out here, but the presence of .title in that :is() selector on top makes the specificity of that selector (0, 0, 2, 0) winning out over the (0, 0, 1, 1) below.

This is where we could consider using :where() instead! If we were to use the :where() pseudo-selector instead:

.card :where(.title, p) {   color: red; }  .card p {   color: yellow; }

Then yellow would win, because the top selector lowers to (0, 0, 1, 0) specificity, losing to the bottom selector’s (0, 0, 1, 1).

Which one should you use? Ya know what, I’m not really sure if there is super solid time-tested advice here. At least we have both options available, meaning if you get into a pickle, you’ve got tools to use. Time has taught me that keeping specificity low is generally a healthier place to be. That gives you room to override, where if you’re riding high with specificity you have fewer options. But the zero specificity of :where() is pretty extreme and I could see that leading to confusing moments, too. So my gut is telling me you might wanna start with :is(), unless you notice you need to mix in a higher-specificity selector; if you do, back off to :where().


The post :where() has a cool specificity trick, too. appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Servers: Cool Once Again

There were jokes coming back from the holiday break that JavaScript decided to go all server-side. I think it was rooted in:

  • The Basecamp gang releasing Hotwire, which looks like marketing panache around a combination of technologies. “HTML over the wire,” they say, meaning it makes the server generate and serve HTML, and leaves client-side JavaScript to things only client-side JavaScript can do.
  • The React gang Introducing Zero-Bundle-Size React Server Components, which I believe is the first step of the core project toward server-side anything.

I’m all about some marketing hype, but it’s worth noting that these are just fresh takes on already solid (dare I say old) ideas.

Turbo (“The heart of Hotwire”) is an evolution of Turbolinks, which is a terrifically simple base idea: intercept clicks on internal links. Rather than the browser doing a full page refresh, fetch the contents of the new page, plop it in place, and History.pushState() the URL. Now you’ve got a Single Page App feel, but you didn’t have to build a SPA. That’s mighty convenient if you’ve already built your app in Rails with ERB templates.

But is that actually efficient? Well, it hasn’t been particularly popular so far. The thinking has been that the network is the bottleneck, so let’s send as little as possible over the network. “As little as possible” typically translates into JSON, typically. If you get JSON on the client, now you need a templating system on the client to turn that into usable DOM. With that technique, you’re paying two costs: 1) loading a client-side library 2) data-to-DOM processing. If you sent “HTML over the wire,” you pay neither of those costs (faster), but theoretically are sending beefier payloads across the network (slower), which assumes that HTML is heavier than JSON, which is… questionable.

So… it depends. It depends on how big the payloads are and what is expected to be done with them.

You’d expect the React opinion would be: definitely use the client. But that’s not true with the new preview of server side components. The video is abundantly clear: “rendering” the components on the server is faster, particularly in nested component situations where many of the components are responsible for fetching their own data. So what comes across the network then? Is it DOM-ready HTML? Not here. From a peek at the video, it looks like the network response is some proprietary format that describes a React component. That seems important because it means the client-side JavaScript bundle doesn’t contain that component at all, and state can be passed back and forth. Lauren Tan is also clear in the video: this is kinda SSR but distinct from how something, like Next.js, does SSR today. And the point is to make the Next.js of tomorrow far better.

So: servers. They are just good at doing certain things (says the guy typing into his WordPress blog). There does seem to be some momentum toward doing less on the client, which I think most of us would agree has been taking on a bit much lately, which asset sizes doing nothing but growing and growing.

Let’s push those servers to the edge while we’re at it.


The post Servers: Cool Once Again appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Cool Little CSS Grid Tricks for Your Blog

I discovered CSS about a decade ago while trying to modify the look of a blog I had created. Pretty soon, I was able to code cool things with more mathematical and, therefore, easier-to-understand features like transforms. However, other areas of CSS, such as layout, have remained a constant source of pain.

This post is about a problem I encountered about a decade ago and, until recently, did not know how to solve in a smart way. Specifically, it’s about how I found a solution to a long-running problem using a modern CSS grid technique that, in the process, gave me even cooler results than I originally imagined.

That this is not a tutorial on how to best use CSS grid, but more of a walk through my own learning process.

The problem

One of the first things I used to dump on that blog were random photos from the city, so I had this idea about having a grid of thumbnails with a fixed size. For a nicer look, I wanted this grid to be middle-aligned with respect to the paragraphs above and below it, but, at the same time, I wanted the thumbnails on the last row to be left-aligned with respect to the grid. Meanwhile, the width of the post (and the width of the grid within it) would depend on the viewport.

The HTML looks something like this:

<section class='post__content'>   <p><!-- some text --></p>   <div class='grid--thumbs'>     <a href='full-size-image.jpg'>       <img src='thumb-image.jpg' alt='image description'/>     </a>     <!-- more such thumbnails -->   </div>   <p><!-- some more text --></p> </section>

It may seem simple, but it turned out to be one of the most difficult CSS problems I’ve ever encountered.

Less than ideal solutions

These are things I have tried or seen suggested over the years, but that never really got me anywhere.

Floating impossibility

Floats turned out to be a dead end because I couldn’t figure out how to make the grid be middle aligned this way.

.grid--thumbs { overflow: hidden; }  .grid--thumbs a { float: left; }

The demo below shows the float attempt. Resize the embed to see how they behave at different viewport widths.

inline-block madness

At first, this seemed like a better idea:

.grid--thumbs { text-align: center }  .grid--thumbs a { display: inline-block }

Except it turned out it wasn’t:

The last row isn’t left aligned in this case.

At a certain point, thanks to an accidental CSS auto-complete on CodePen, I found out about a property called text-align-last, which determines how the last line of a block is aligned.

Unfortunately, setting text-align-last: left on the grid wasn’t the solution I was looking for either:

At this point, I actually considered dropping the idea of a middle aligned grid. Could a combo of text-align: justified and text-align-last: left on the grid produce a better result?

Well, turns out it doesn’t. That is, unless there’s only a thumbnail on the last row and the gaps between the columns aren’t too big. Resize the embed below to see what I mean.

This is pretty where I was at two years ago, after nine years of trying and failing to come up with a solution to this problem.

Messy flexbox hacks

A flexbox solution that seemed like it would work at first was to add an ::after pseudo-element on the grid and set flex: 1 on both the thumbnails and this pseudo-element:

.grid--thumbs {   display: flex;   flex-wrap: wrap; 	   a, &::after { flex: 1; } 	   img { margin: auto; } 	   &:after { content: 'AFTER'; } }

The demo below shows how this method works. I’ve given the thumbnails and the ::after pseudo-element purple outlines to make it easier to see what is going on.

This is not quite what I wanted because the grid of thumbnails is not middle-aligned. Thats said, it doesn’t look too bad… as long as the last row has exactly one item less image than the others. As soon as that changes, however, the layout breaks if it’s missing more items or none.

Screenshot collage. Shows how the layout breaks when the last row is not missing exactly one item to be full.
Why the ::after hack is not reliable.

That was one hacky idea. Another is to use a pseudo-element again, but add as many empty divs after the thumbnails as there are columns that we’re expecting to have. That number is something we should be able to approximate since the size of the thumbnails is fixed. We probably want to set a maximum width for the post since text that stretches across the width of a full screen can visually exhausting for eyes to read.

The first empty elements will take up the full width of the row that’s not completely filled with thumbnails, while the rest will spill into other rows. But since their height is zero, it won’t matter visually.

This kind of does the trick but, again, it’s hacky and still doesn’t produce the exact result I want since it sometimes ends up with big and kind of ugly-looking gaps between the columns.

A grid solution?

The grid layout has always sounded like the answer, given its name. The problem was that all examples I had seen by then were using a predefined number of columns and that doesn’t work for this particular pattern where the number of columns is determined by the viewport width.

Last year, while coding a collection of one element, pure CSS background patterns, I had the idea of generating a bunch of media queries that would modify a CSS variable, --n, corresponding to the number of columns used to set grid-template-columns.

$ w: 13em; $ h: 19em; $ f: $ h/$ w; $ n: 7; $ g: 1em;  --h: #{$ f*$ w}; display: grid; grid-template-columns: repeat(var(--n, #{$ n}), var(--w, #{$ w})); grid-gap: $ g; place-content: center; 	 @for $ i from 1 to $ n {   @media (max-width: ($ n - $ i + 1)*$ w + ($ n - $ i + 2)*$ g) {     --n: #{$ n - $ i}   } }

I was actually super proud of this idea at the time, even though I cringe looking back on it now. One media query for every number of columns possible is not exactly ideal, not to mention it doesn’t work so well when the grid width doesn’t equal the viewport width, but is still somewhat flexible and also depends on the width of its siblings.

A magic solution

I finally came across a better solution while working with CSS grid and failing to understand why the repeat() function wasn’t working in a particular situation. It was so frustrating and prompted me to go to MDN, where I happened to notice the auto-fit keyword and, while I didn’t understand the explanation, I had a hunch that it could help with this other problem, so I dropped everything else I was doing and gave it a try.

Here’s what I got:

.grid--thumbs {   display: grid;   justify-content: center;   grid-gap: .25em;   grid-template-columns: repeat(auto-fit, 8em); }

I also discovered the minmax() function, which can be used in place of fixed sizes on grid items. I still haven’t been able to understand exactly how minmax() works — and the more I play with it, the less I understand it — but what it looks like it does in this situation is create the grid then stretch its columns equally until they fill all of the available space:

grid-template-columns: repeat(auto-fit, minmax(8em, 1fr));

Another cool thing we can do here is prevent the image from overflowing when it’s wider than the grid element. We can do this by replacing the minimum 8em with min(8em, 100%) That essentially ensures that images will never exceed 100%, but never below 8em. Thanks to Chris for this suggestion!

Note that the min() function doesn’t work in pre-Chromium Edge!

Keep in mind that this only produces a nice result if all of the images have the same aspect ratio — like the square images I’ve used here. For my blog, this was not an issue since all photos were taken with my Sony Ericsson W800i phone, and they all had the same aspect ratio. But if we were to drop images with different aspect ratios, the grid wouldn’t look as good anymore:

We can, of course, set the image height to a fixed value, but that distorts the images… unless we set object-fit to cover, which solves our problem!

Another idea would be to turn the first thumbnail into a sort of banner that spans all grid columns. The one problem is that we don’t know the number of columns because that depends on the viewport. But, there is a solution — we can set grid-column-end to -1!

.grid--thumbs {   /* same styles as before */ 	   a:first-child {     grid-column: 1/ -1; 		     img { height: 13em }   } }

The first image gets a bigger height than all the others.

Of course, if we wanted the image to span all columns except the last, one we’d set it to -2 and so on… negative column indices are a thing!

auto-fill is another grid property keyword I noticed on MDN. The explanations for both are long walls of text without visuals, so I didn’t find them particularly useful. Even worse, replacing auto-fit with auto-fill in any of the grid demos above produces absolutely no difference. How they really work and how they differ still remains a mystery, even after checking out articles or toying with examples.

However, trying out different things and seeing what happens in various scenarios at one point led me to the conclusion that, if we’re using a minmax() column width and not a fixed one (like 8em), then it’s probably better to use auto-fill instead of auto-fit because, the result looks better if we happen to only have a few images, as illustrated by the interactive demo below:

I think what I personally like best is the initial idea of a thumbnail grid that’s middle-aligned and has a mostly fixed column width (but still uses min(100%, 15em) instead of just 15em though). At the end of the day, it’s a matter of personal preference and what can be seen in the demo below just happens to look better to me:

I’m using auto-fit in this demo because it produces the same result as auto-fill and is one character shorter. However, what I didn’t understand when making this is that both keywords produce the same result because there are more items in the gallery than we need to fill a row.

But once that changes, auto-fit and auto-fill produce different results, as illustrated below. You can change the justify-content value and the number of items placed on the grid:

I’m not really sure which is the better choice. I guess this also depends on personal preference. Coupled with justify-content: center, auto-fill seems to be the more logical option, but, at the same time, auto-fit produces a better-looking result.

The post Cool Little CSS Grid Tricks for Your Blog appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

It’s pretty cool how Netlify CMS works with any flat file site generator

Little confession here: when I first saw Netlify CMS at a glance, I thought: cool, maybe I’ll try that someday when I’m exploring CMSs for a new project. Then as I looked at it with fresh eyes: I can already use this! It’s a true CMS in that it adds a content management UI on top of any static site generator that works from flat files! Think of how you might build a site from markdown files with Gatsby, Jekyll, Hugo, Middleman, etc. You can create and edit Markdown files and the site’s build process runs and the site is created.

Netlify CMS gives you (or anyone you set it up for) a way to create/edit those Markdown files without having to use a code editor or know about Pull Requests on GitHub or anything. It’s a little in-browser app that gives you a UI and does the file manipulation and Git stuff behind the scenes.

Here’s an example.

Our conferences website is a perfect site to build with a static site generator.

It’s on GitHub, so it’s open to Pull Requests, and each conference is a Markdown file.

That’s pretty cool already. The community has contributed 77 Pull Requests already really fleshing out the content of the site, and the design, accessibility, and features as well!

I used 11ty to build the site, which works great with building out those Markdown files into a site, using Nunjucks templates. Very satisfying combo, I found, after a slight mostly configuration-related learning curve.

Enter Netlify CMS.

But as comfortable as you or I might be with a quick code edit and Pull Request, not everybody is. And even I have to agree that going to a URL quick, editing some copy in input fields, and clicking a save button is the easiest possible way to manage content.

That CMS UI is exactly what Netlify CMS gives you. Wanna see the entire commit for adding Netlify CMS?

It’s two files! That still kinda blows my mind. It’s a little SPA React app that’s entirely configurable with one file.

Cutting to the chase here, once it is installed, I now have a totally customized UI for editing the conferences on the site available right on the production site.

Netlify CMS doesn’t do anything forceful or weird, like attempt to edit the HTML on the production site directly. It works right into the workflow in the same exact way that you would if you were editing files in a code editor and committing in Git.

Auth & Git

You use Netlify CMS on your production site, which means you need authentication so that just you (and the people you want) have access to it. Netlify Identity makes that a snap. You just flip it on from your Netlify settings and it works.

I activated GitHub Auth so I could make logging in one-click for me.

The Git magic happens through a technology called Git Gateway. You don’t have to understand it (I don’t really), you just enable it in Netlify as part of Netlify Identity, and it forms the connection between your site and the Git repository.

Now when you create/edit content, actual Markdown files are created and edited (and whatever else is involved, like images!) and the change happens right in the Git repository.


I made this the footer of the site cause heck yeah.

The post It’s pretty cool how Netlify CMS works with any flat file site generator appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , , ,
[Top]

The Whole Spreadsheets as Databases Thing is Pretty Cool

A spreadsheet has always been a strong (if fairly literal) analogy for a database. A database has tables, which is like a single spreadsheet. Imagine a spreadsheet for tracking RSVPs for a wedding. Across the top, column titles like First Name, Last Name, Address, and Attending?. Those titles are also columns in a database table. Then each person in that spreadsheet is literally a row, and that’s also a row in a database table (or an entry, item, or even tuple if you’re really a nerd).

It’s been getting more and more common that this doesn’t have to be an analogy. We can quite literally use a spreadsheet UI to be our actual database. That’s meaningful in that it’s not just viewing database data as a spreadsheet, but making spreadsheet-like features first-class citizens of the app right alongside database-like features.

With a spreadsheet, the point might be viewing the thing as a whole and understanding things that way. Browsing, sorting, entering and editing data directly in the UI, and making visual output that is useful.

With a database, you don’t really look right at it — you query it and use the results. Entering and editing data is done through code and APIs.

That’s not to say you can’t look directly at a database. Database tools like Sequel Pro (and many others!) offer an interface for looking at tables in a spreadsheet-like format:

What’s nice is that the idea of spreadsheets and databases can co-exist, offering the best of both worlds at once. At least, on a certain scale.

We’ve talked about Airtable before here on CSS-Tricks and it’s a shining example of this.

Airtable calls them bases, and while you can view the data inside them in all sorts of useful ways (a calendar! a gallery! a kanban!), perhaps the primary view is that of a spreadsheet:

If all you ever do with Airtable is use it as a spreadsheet, it’s still very nice. The UI is super well done. Things like filtering and sorting feel like true first-class citizens in a way that it’s almost weird that other spreadsheet technology doesn’t. Even the types of fields feel practical and modern.

Plus with all the different views in a base, and even cooler, all the “blocks” they offer to make the views more dashboard-like, it’s a powerful tool.

But the point I’m trying to make here is that you can use your Airtable base like a database as well, since you automatically have read/write API access to your base.

So cool that these API docs use data from your own base to demonstrate the API.

I talked about this more in my article How To Use Airtable as a Front End Developer. This API access is awesome from a read data perspective, to do things like use it as a data source for a blog. Robin yanked in data to build his own React-powered interface. I dig that there is a GraphQL interface, if it is third-party.

The write access is arguably even more useful. We use it at CodePen to do CRM-ish stuff by sending data into an Airtable base with all the information we need, then use Airtable directly to visualize things and do the things we want.

Airtable alternatives?

There used to be Fieldbook, but that shut down.

RowShare looks weirdly similar (although a bit lighter on features) but it doesn’t look like it has an API, so it doesn’t quite fit the bill for that database/spreadsheet gap spanning.

Zoho Creator does have an API and interesting visualization stuff built in, which actually looks pretty darn cool. It looks like some of their marketing is based around the idea that if you need to build a CRUD app, you can do that with this with zero coding — and I think they are right that it’s a compelling sell.

Actiondesk looks interesting in that it’s in the category of a modern take on the power of spreadsheets.

While it’s connected to a database in that it looks like it can yank in data from something like MySQL or PostgreSQL, it doesn’t look like it has database-like read/write APIs.

Can we just use Google Sheets?

The biggest spreadsheet tool in the sky is, of course, the Google one, as it’s pretty good, free, and familiar. It’s more like a port of Excel to the browser, so I might argue it’s more tied to the legacy of number-nerds than it is any sort of fresh take on a spreadsheet or data storage tool.

Google Sheets has an API. They take it fairly seriously as it’s in v4 and has a bunch of docs and guides. Check out a practical little tutorial about writing to it from Slack. The problem, as I understand it, is that the API is weird and complicated and hard, like Sheets itself. Call me a wimp, but this quick start is a little eye-glazing.

What looks like the most compelling route here, assuming you want to keep all your data in Google Sheets and use it like a database, is Sheetsu. It deals with the connection/auth to the sheet on its end, then gives you API endpoints to the data that are clean and palatable.

Plus there are some interesting features, like giving you a form UI for possibly easier (or more public) data entry than dealing with the spreadsheet itself.

There is also Sheetrock.js, an open source library helping out with that API access to a sheet, but it hasn’t been touched in a few years so I’m unsure the status there.

I ain’t trying to tell you this idea entirely replaces traditional databases.

For one thing, the relational part of databases, like MySQL, is a super important aspect that I don’t think spreadsheets always handle particularly well.

Say you have an employee table in your database, and for each row in that table, it lists the department they work for.

ID  Name                  Department --  --                    -- 1   Chris Coyier          Front-End Developer 2   Barney Butterscotch   Human Resources   

In a spreadsheet, perhaps those department names are just strings. But in a database, at a certain scale, that’s probably not smart. Instead, you’d have another table of departments, and relate the two tables with a foreign key. That’s exactly what is described in this classic explainer doc:

To find the name of a particular employee’s department, there is no need to put the name of the employee’s department into the employee table. Instead, the employee table contains a column holding the department ID of the employee’s department. This is called a foreign key to the department table. A foreign key references a particular row in the table containing the corresponding primary key.

ID  Name                  Department --  --                    -- 1   Chris Coyier          1 2   Barney Butterscotch   2   ID  Department            Manager     --  --                    -- 1   Front-End Developers  Akanya Borbio 2   Human Resources       Susan Snowrinkle

To be fair, spreadsheets can have relational features too (Airtable does), but perhaps it isn’t a fundamental first-class citizen like some databases treat it.

Perhaps more importantly, databases, largely being open source technology, are supported by a huge ecosystem of technology. You can host your PostgreSQL or MySQL database (or whatever all the big database players are) on all sorts of different hosting platforms and hardware. There are all sorts of tools for monitoring it, securing it, optimizing it, and backing it up. Plus, if you’re anywhere near breaking into the tens of thousands of rows point of scale, I’d think a spreadsheet has been outscaled.

Choosing a proprietary host of data is largely for convenience and fancy UX at a somewhat small scale. I kinda love it though.

The post The Whole Spreadsheets as Databases Thing is Pretty Cool appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]