Category: Design

style9: build-time CSS-in-JS

In April of last year, Facebook revealed its big new redesign. An ambitious project, it was a rebuild of a large site with a massive amount of users. To accomplish this, they used several technologies they have created and open-sourced, such as React, GraphQL, Relay, and a new CSS-in-JS library called stylex.

This new library is internal to Facebook, but they have shared enough information about it to make an open-source implementation, style9, possible.

Why another CIJ library?

There are already plenty of CSS-in-JS (CIJ) libraries, so it might not be obvious why another one is needed. style9 has the same benefits as all other CIJ solutions, as articulated by Christopher Chedeau, including scoped selectors, dead code elimination, deterministic resolution, and the ability to share values between CSS and JavaScript.

There are, however, a couple of things that make style9 unique.

Minimal runtime

Although the styles are defined in JavaScript, they are extracted by the compiler into a regular CSS file. That means that no styles are shipped in your final JavaScript file. The only things that remain are the final class names, which the minimal runtime will conditionally apply, just like you would normally do. This results in smaller code bundles, a reduction in memory usage, and faster rendering.

Since the values are extracted at compile time, truly dynamic values can’t be used. These are thankfully not very common and since they are unique, don’t suffer from being defined inline. What’s more common is conditionally applying styles, which of course is supported. So are local constants and mathematical expressions, thanks to babel’s path.evaluate.

Atomic output

Because of how style9 works, every property declaration can be made into its own class with a single property. So, for example, if we use opacity: 0 in several places in our code, it will only exist in the generated CSS once. The benefit of this is that the CSS file grows with the number of unique declarations, not with the total amount of declarations. Since most properties are used many times, this can lead to dramatically smaller CSS files. For example, Facebook’s old homepage used 413 KB of gzipped CSS. The redesign uses 74 KB for all pages. Again, smaller file size leads to better performance.

Slide from Building the New Facebook with React and Relay by Frank Yan, at 13:23 showing the logarithmic scale of atomic CSS.

Some may complain about this, that the generated class names are not semantic, that they are opaque and are ignoring the cascade. This is true. We are treating CSS as a compilation target. But for good reason. By questioning previously assumed best practices, we can improve both the user and developer experience.

In addition, style9 has many other great features, including: typed styles using TypeScript, unused style elimination, the ability to use JavaScript variables, and support for media queries, pseudo-selectors and keyframes.

Here’s how to use it

First, install it like usual:

npm install style9

style9 has plugins for Rollup, Webpack, Gatsby, and Next.js, which are all based on a Babel plugin. Instructions on how to use them are available in the repository. Here, we’ll use the webpack plugin.

const Style9Plugin = require('style9/webpack'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');  module.exports = {   module: {     rules: [       // This will transform the style9 calls       {         test: /.(tsx|ts|js|mjs|jsx)$ /,         use: Style9Plugin.loader       },       // This is part of the normal Webpack CSS extraction       {         test: /.css$ /i,         use: [MiniCssExtractPlugin.loader, 'css-loader']       }     ]   },   plugins: [     // This will sort and remove duplicate declarations in the final CSS file     new Style9Plugin(),     // This is part of the normal Webpack CSS extraction     new MiniCssExtractPlugin()   ] };

Defining styles

The syntax for creating styles closely resembles other libraries. We start by calling style9.create with objects of styles:

import style9 from 'style9';  const styles = style9.create({   button: {     padding: 0,     color: 'rebeccapurple'   },   padding: {     padding: 12   },   icon: {     width: 24,     height: 24   } });

Because all declarations will result in atomic classes, shorthands such as flex: 1 and background: blue won’t work, as they set multiple properties. Properties that can be can be expanded, such as padding, margin, overflow, etc. will be automatically converted to their longhand variants. If you use TypeScript, you will get an error when using unsupported properties.

Resolving styles

To generate a class name, we can now call the function returned by style9.create. It accepts as arguments the keys of the styles we want to use:

const className = styles('button');

The function works in such a way that styles on the right take precedence and will be merged with the styles on the left, like Object.assign. The following would result in an element with a padding of 12px and with rebeccapurple text.

const className = styles('button', 'padding');

We can conditionally apply styles using any of the following formats:

// logical AND styles('button', hasPadding && 'padding'); // ternary styles('button', isGreen ? 'green' : 'red'); // object of booleans styles({   button: true,   green: isGreen,   padding: hasPadding });

These function calls will be removed during compilation and replaced with direct string concatenation. The first line in the code above will be replaced with something like 'c1r9f2e5 ' + hasPadding ? 'cu2kwdz ' : ''. No runtime is left behind.

Combining styles

We can extend a style object by accessing it with a property name and passing it to style9.

const styles = style9.create({ blue: { color: 'blue; } }); const otherStyles = style9.create({ red: { color: 'red; } });  // will be red const className = style9(styles.blue, otherStyles.red);

Just like with the function call, the styles on the right take precedence. In this case, however, the class name can’t be statically resolved. Instead the property values will be replaced by classes and will be joined at runtime. The properties gets added to the CSS file just like before.

Summary

The benefits of CSS-in-JS are very much real. That said, we are imposing a performance cost when embedding styles in our code. By extracting the values during build-time we can have the best of both worlds. We benefit from co-locating our styles with our markup and the ability to use existing JavaScript infrastructure, while also being able to generate optimal stylesheets.

If style9 sounds interesting to you, have a look a the repo and try it out. And if you have any questions, feel free to open an issue or get in touch.

Acknowledgements

Thanks to Giuseppe Gurgone for his work on style-sheet and dss, Nicolas Gallagher for react-native-web, Satyajit Sahoo and everyone at Callstack for linaria, Christopher Chedeau, Sebastian McKenzie, Frank Yan, Ashley Watkins, Naman Goel, and everyone else who worked on stylex at Facebook for being kind enough to share their lessons publicly. And anyone else I have missed.

Links


The post style9: build-time CSS-in-JS appeared first on CSS-Tricks.

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

CSS-Tricks

, ,

A Bit on Web Component Libraries

A run of Web Components news crossed my desk recently so I thought I’d group it up here.

To my mind, one of the best use cases for Web Components is pattern libraries. Instead of doing, say, <ul class="nav nav-tabs"> like you would do in Bootstrap or <div class="tabs"> like you would in Bulma, you would use a custom element, like <designsystem-tabs>.

The new Shoelace library uses the sl namespace for their components. It’s a whole pattern library entirely in Web Components. So the tabs there are <sl-tab-group> elements.

Why is that good? Well, for one thing, it brings a component model to the party. That means, if you’re working on a component, it has a template and a stylesheet that are co-located. Peeking under the hood of Shoelace, you can see this is all based on Stencil.

Another reason it’s good is that it means components can (and they do) use the Shadow DOM. This offers a form of isolation that comes right from the web platform. For CSS folks like us, that means the styling for a tab in the tab component is done with a .tab class (hey, wow, cool) but it is isolated in that component. Even with that generic of a name, I can’t accidentally mess with some other component on the page that uses that generic class, nor is some other outside CSS going to mess with the guts here. The Shadow DOM is a sort of wall of safety that prevents styles from leaking out or seeping in.

I just saw the FAST framework¹ too, which is also a set of components. It has tabs that are defined as <fast-tabs>. That reminds me of another thing I like about the Web Components as a pattern library approach: if feels like it’s API-driven, even starting with the name of the component itself, which is literally what you use in the HTML. The attributes on that element can be entirely made up. It seems the emerging standard is that you don’t even have to data-* prefix the attributes that you also make up to control the component. So, if I were to make a tabs component, it might be <chris-tabs active-tab="lunch" variation="rounded">.

Perhaps the biggest player using Web Components for a pattern library is Ionic. Their tabs are <ion-tabs>, and you can use them without involving any other framework (although they do support Angular, React, and Vue in addition to their own Stencil). Ionic has made lots of strides with this Web Components stuff, most recently supporting Shadow Parts. Here’s Brandy Carney explaining the encapsulation again:

Shadow DOM is useful for preventing styles from leaking out of components and unintentionally applying to other elements. For example, we assign a .button class to our ion-button component. If an Ionic Framework user were to set the class .button on one of their own elements, it would inherit the Ionic button styles in past versions of the framework. Since ion-button is now a Shadow Web Component, this is no longer a problem.

However, due to this encapsulation, styles aren’t able to bleed into inner elements of a Shadow component either. This means that if a Shadow component renders elements inside of its shadow tree, a user isn’t able to target the inner element with their CSS.

The encapsulation is a good thing, but indeed it does make styling “harder” (on purpose). There is an important CSS concept to know: CSS custom properties penetrate the Shadow DOM. However, it was decided — and I think rightly so — that “variablizing” every single thing in a design system is not a smart way forward. Instead, they give each bit of HTML inside the Shadow DOM a part, like <div part="icon">, which then gives gives the ability to “reach in from the outside” with CSS, like custom-component::part(icon) { }.

I think part-based styling hooks are mostly fine, and a smart way forward for pattern libraries like this, but I admit some part of it bugs me. The selectors don’t work how you’d expect. For example, you can’t conditionally select things. You also can’t select children or use the cascade. In other words, it’s just one-off, or like you’re reaching straight through a membrane with your hand. You can reach forward and either grab the thing or not, but you can’t do anything else at all.

Speaking of things that bug people, Andrea Giammarchi has a good point about the recent state of Web Components:

Every single library getting started, including mine, suggest we should import the library in order to define what [sic] supposed to be a “portable Custom Element”.

Google always suggests LitElement. Microsoft wants you to use FASTElement. Stencil has their own Component. hyperHTML has their own Component. Nobody is just using “raw” Web Components. It’s weird! What strikes me as the worst part about that is that Web Components are supposed to be this “native platform” thing meaning that we shouldn’t need to buy into some particular technology in order to use them. When we do, we’re just as locked to that as we would be if we just used React or whatever.

Andrea has some ideas in that article, including the use of some new and smaller library. I think what I’d like to see is a pattern library that just doesn’t use any library at all.

  1. FAST calls itself a “interface system,” then a “UI framework” in consecutive sentences on the homepage. Shoelaces calls itself a “library” but I’m calling it a “pattern library.” I find “design system” to be the most commonly used term to describe the concept, but often used more broadly than a specific technology. FAST uses that term in the code itself for the wrapper element that controls the theme. I’d say the terminology around all this stuff is far from settled.

The post A Bit on Web Component Libraries appeared first on CSS-Tricks.

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

CSS-Tricks

,
[Top]

A’ Design Awards & Competition – Standard Call for Entries

[Top]

Want to get better at code? Teach someone CSS.

A friend of mine recently asked me to teach her to code. She was an absolute beginner, having no idea what coding really involves. I decided to start where I started: HTML and CSS. Using CodePen, we started forking Pens and altering them. Soon, a learning path started to unravel.

The aim of this article is not to teach basic CSS to those who already know it but rather to highlight the things that inspired a newcomer and hopefully inspire you to pass on some knowledge to others if the opportunity arises. It felt good to help someone out and, in turn, I learned some really valuable lessons that have changed the way I think about my code. Win win!

So, here we go: five lessons I learned from teaching someone CSS.

Lesson 1: Don’t start from scratch

When I starting coding for the web 12 years ago, I began with layout — positioning with floats, margins, padding and position declarations. It might seem outdated these days, but still, this is where I went right away with my new coding buddy.

It didn’t go that well.

As you might guess, starting with something like, “Here is how to position an empty box in the middle of the screen,” was a mistake. How uninspiring! And even though I was impressed with my own ability to demonstrate how Flexbox can position an element in the center of the screen (more on that later), I was immediately faced with lots of additional, non-positional questions.

“So how do you change the colors?”

“Can it change shape when hovered over?”

“What fonts can you use on the web?”

I thought we were weeks away from all that.

So, my plans of teaching the 12-column grid went out the window and we pulled up Chris’ named color chart alongside a couple of forked Pens and started playing around. First off, we changed the colors of Cassidy Williams Netflix/Netlify logo. Wow! Instant hit.

<a class="container" href="https://netlify.com" target="_blank">    <div class="logo">     <div class="uno"></div>     <div class="dos"></div>     <div class="tres"></div>   </div>   <div class="name">Prettier</div> </a>

Then a few simple tweaks to the CSS:

body {   background: #F9F2DB;   color: #092935;   font-size: 50px; } 
 a {   color: #092935; } 
 .logo .uno, .dos, .tres {   background: #C61561; }  .logo .dos {   box-shadow: 0 0 20px #F9F2DB; }  .logo::before {   background: #F9F2DB; } 
 .name {   letter-spacing: 8px; }

Within minutes, my friend was hooked! There was no boring positioning to worry about, just a clear example of how a few simple lines of code can change something so familiar into something entirely different.

Then it kicked in that you can change the color of anything! We loaded up a couple of well known sites in the browser and changed the colors of some text and backgrounds with DevTools, all in a couple of minutes. Mission accomplished! My friend was hooked. 

Lesson learned: Don’t worry about trying to build something from scratch. Have a play with what’s already out there! 

Lesson 2: Comments

This isn’t where I had planned to go with my planned class, but the question of why some parts of CSS start with /* and end with */ came up, so we went with it. 

This one really had me thinking about my own work. I really do not comment my code enough. Watching a new coder comment everything (and I mean everything) reminded me just how helpful comments are, not only for yourself, but also to a wider team, or even future you. (Sarah Drasner has a great talk on this topic).

And here is the thing: until then, I thought I was commenting pretty diligently. However, watching someone else do it made me realize how many times I look at a piece of code (particularly JavaScript) and wish I had put a line or two in there to remind myself what I was doing. A ten-second task might have saved me five (or perhaps even more) minutes down the road. That adds up and is now something I am working on.

Lesson learned: Comment more. 

Lesson 3: Positioning

We started with some basic HTML, and honestly, I saw my friend’s eyes glazing over almost immediately. It just looks so dull when you can’t see it doing anything right away (unlike editing pre-written CSS). However, we stuck with it, and got results.

Take my word for it, don’t start by positioning empty <div> elements with 1-pixel borders around them. You’ll lose your audience very quickly. Put a picture of a dog in there — or baby Yoda or a pizza — just as long as it’s anything other than an empty element.

We then turned to Flexbox. We actually found CSS Grid a bit too much at the start. We looked briefly at CSS Grid, but when reading lots of articles about it, it’s clear that many assume the reader already has familiarity with CSS, Flexbox in particular. My friend decided to start with Flexbox.

An admission on my part: I am so used to using UI frameworks (especially Bootstrap) that I very rarely position anything in Flexbox by writing the CSS myself. I know how it works and (most of) the declarations, but I still very rarely write it out myself, even in situations where it would be relatively easy. Teaching made me think about my reliance on UI frameworks as a whole. Yes, they are undoubtedly amazing and save us tons of time on projects, but I recalled using Bootstrap on a recent project that was essentially two pages and probably didn’t need it! 

Lesson learned: If the project is something small with a minimal number of elements to position, then consider ditching the framework and code from scratch! The end result will be lightweight, fast, and way more satisfying!

Lesson 4: Typography

I love typography. I’ve been lucky enough to work with great designers over the past few years and that has really helped me dial in on the nuances of type. It’s amazing how changes to things like line-height and letter-spacing can really help lift a design from average to amazing. This was something I was keen to impress upon my eager new student. Well, I needn’t have bothered, as the only thing of interest (initially) was changing the fonts and then, crucially for me, the sheer number of fonts available for us to use. The choices are almost limitless and the services and foundries offering web fonts have exploded in the past few years to a point where anything is possible, at speed with little impact on load times.

But here is the thing about designers (and front-end developers like myself): we can be a bit narrow-minded in our font choices. Designs tend to stick to the same fonts from the same services (Roboto and Open Sans anyone?) because we know they are easy to implement and that they work. Exploring fonts with someone new to the trade forced me to look beyond the old staples and try a few new things. I’m now looking for new pairings that work together and dialing in on how they work on screen and impact the whole look and feel of a design. In short, teaching someone else about type has improved my own journey with type, which was probably stuck in something like 2017. 

Lesson learned: Keep up to date with type.

Lesson 5. :hover makes everything fun

Things were going OK up to this point, but as you can probably imagine, things were still pretty static. Without really planning, we stumbling into adding a hover effect on on an element and it was an instant hook, just like it was changing colors for the first time!

Hovers add interaction and easily impress, which makes them great for a newcomer to play around with. Scaling objects, changing a box from square to round, hiding content — these are the types of thing that can all be done so easily that hovers are an ideal way for a new coder to get instant results. And here’s the thing: “‘playing” around like this opens other doors. “What if I just do this?” is something many us rarely get to ask ourselves in our day-to-day jobs. With defined designs to work from, there is often little chance to play and equally less chance to experiment.

So, here is the final lesson: Make time to play. Just by being asked, “How do you make this thing do that?” has forced me to learn new things, see what’s new in CSS, and see what I can take back into my day-to-day work. Experimenting (or better yet, playing) has made me a better designer, and I’ll be doing more.

Lesson learned: Make time to play.

Conclusion

If my time teaching CSS to a newbie has taught me anything, it’s that I rarely write code from scratch anymore. Code snippets and autocomplete save me hours, but it’s those same conveniences that let me forget about some really basic stuff. Stuff I should know. By teaching someone else, even if just for 15 minutes now and then, my coding has generally improved and my eyes are open to new ideas and techniques that I may not have otherwise considered.

And as for my friend? Well, she was so taken by CSS in our short time together that she is now doing an online course that includes HTML, which doesn’t seem so dull now that she knows what it is capable of doing!


The post Want to get better at code? Teach someone CSS. appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

The GitHub Profile Trick

Monica Powell shared a really cool trick the other day:

The profile README is created by creating a new repository that’s the same name as your username. For example, my GitHub username is m0nica so I created a new repository with the name m0nica.

Now the README.md from that repo is essentially the homepage of her profile. Above the usual list of popular repos, you can see the rendered version of that README on her profile:

Lemme do a lame version for myself real quick just to try it out…

OK, I start like this:

Screenshot of the default profile page for Chris Coyier.

Then I’ll to go repo.new (hey, CodePen has one of those cool domains too!) and make a repo on my personal account that is exactly the same as my username:

Screenshot showing the create new repo screen on GitHub. The repository name is set to chriscoyier.

I chose to initialize the repo with a README file and nothing else. So immediately I get:

Screenshot of the code section of the chriscoyier repo, which only contains a read me file that says hi there.

I can edit this directly on the web, and if I do, I see more helpful stuff:

Screenshot of editing the read me file directly in GitHub.

Fortunately, my personal website has a Markdown bio ready to use!

Screenshot of Chris Coyier's personal website homepage. It has a dark background and a large picture of Chris wearing a red CodePen hat next to some text welcoming people to the site.

I’ll copy and paste that over.

Screenshot showing the Markdown code from the personal website in the GitHub editor.

After committing that change, my own profile shows it!

Screenshot of the updated GitHub profile page, showing the welcome text from the personal website homepage.

Maybe I’ll get around to doing something more fun with it someday. Monica’s post has a bunch of fun examples in it. My favorite is Kaya Thomas’ profile, which I saw Jina Anne share:

You can’t use CSS in there (because GitHub strips it out), so I love the ingenuity of using old school <img align="right"> to pull off the floating image look.


The post The GitHub Profile Trick appeared first on CSS-Tricks.

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

CSS-Tricks

, ,
[Top]

CSS Vocabulary

This is a neat interactive page by Ville V. Vanninen to reference the names of things in the CSS syntax. I feel like the easy ones to remember are “selector,” “property,” and “value,” but even as a person who writes about CSS a lot, I forget some of the others. Like the property and value together (with the colon) is called a declaration. And all the declarations together, including the curly brackets (but not the selector)? That’s a declaration block, which is slightly more specific than a block, because a block might be inside an at-rule and thus contain other complete rule-sets.

Direct Link to ArticlePermalink


The post CSS Vocabulary appeared first on CSS-Tricks.

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

CSS-Tricks

[Top]

Bold on Hover… Without the Layout Shift

When you change the font-weight of a font, the text will typically cause a bit of a layout shift. That’s because bold text is often larger and takes up more space. Sometimes that doesn’t matter, like a vertical stack of links where the wider/bolder text doesn’t push anything anyway. Sometimes it does matter, like a horizontal row where the wider/bolder text pushes other elements away a smidge.

Ryan Mulligan demonstrates:

Ryan’s technique is very clever. Each item in the list has a pseudo-element on it with the exact text in the link. That pseudo-element is visually hidden, but pre-bolded and still occupies width. So when the actual link text is bolded, it won’t take up any additional width.

It also sorta depends on how you’re doing the layout. Here, if I force four columns with CSS grid and text that doesn’t really challenge the width, the bolding doesn’t affect the layout either:

But if I were to, say, let those links flow into automatic columns, we would have the shifting problem.


The post Bold on Hover… Without the Layout Shift appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

Using Trello as a Super Simple CMS

Sometimes our sites need a little sprinkling of content management. Not always. Not a lot. But a bit. The CMS market is thriving with affordable, approachable products, so we’re not short of options. Thankfully, it is a very different world to the one that used to force companies to splash out a ga-jillionty-one dollars (not an exact cost: I rounded to the nearest bazillion) for an all-singing, all-dancing, all-integrating, all-personalizing, big-enterprise-certified™ CMS platform.

Sometimes, though, it’s nice to use a really simple tool that anyone updating content on the site is already familiar with, rather than getting to grips with a new CMS. 

I like Trello a lot for managing ideas and tasks. And it has an API. Why not use it as a content source for a web site? I mean, hey, if we can do it with Google Sheets, then what’s to stop us from trying other things?

Hello, Trello

Here’s a simple site to explore. It gets its content from this Trello board and that content is displayed in sections. Each section is populated by the title and description fields of a card in our Trello board.

Two webpages side-by-side. The left is a Trello board with a bright pink background. The right is a screenshot of the build website using Trello data.

Trello uses Markdown, which comes in handy here. Anyone editing content in a Trello card is able to apply basic text formatting and have the same Markdown flow into the site and transformed into HTML by a build process.

Building blocks

I’m a big fan of this model of running a build which pulls content from various feeds and sources, and then mashes them together with a template to generate the HTML of a website. It decouples the presentation from the management of the content (which is where the term “decoupled” comes from in popular modern CMS products). And it means that we are free to craft the website just the way we want with all of the wizzy tricks and techniques we’ve learned here on CSS-Tricks.

Diagram showing the flow of data, going from Trello as JSON to Build where the data and the template are coupled, then finally, to the front end.

Since we pull in the content at build time, we don’t need to worry about the usage quotas or the performance of our data sources if our sites get popular and bring in loads of traffic. And why wouldn’t they? Look how pretty we made them!

I wanna play!

Fine. You can grab a copy of this site’s code and tinker around to your heart’s content. This version includes information on how to create your own Trello board and use it as the source for content for the build.

If you want to walk through how this works first rather than diving right into it yourself, read on.

Discovering the API

Trello has a well-documented API and set of developer resources. There is also a handy Node module to simplify the task of authenticating and interacting with the API. But you can also explore the API by tinkering with the URLs when you are exploring your Trello boards. 

For example, the URL for the Trello board above is:

https://trello.com/b/Zzc0USwZ/hellotrello

If we add .json to that URL, Trello shows us the content represented as JSON. Take a look.

We can use this technique to inspect the underlying data throughout Trello. Here is the URL for one card in particular:

https://trello.com/c/YVxlSEzy/4-sections-from-cards

If we use this little trick and add .json to the URL we’ll see the data which describes that card

We’ll find interesting things — unique IDs for the board, the list, and the card. We can see the card’s content, and lots of metadata.

I love doing this! Look at all the lovely data! How shall we use it?

Deciding how to use a board

For this example, let’s assume that we have a site with just one page of manageable content. A list or column in our board would be ideal for controlling the sections on that page. An editor could give them titles and content, and drag them around into the order they want.

We’ll need the ID of the list so that we can access it via the API. Luckily, we’ve already seen how to discover that — take a look at the data for any of the cards in the list in question. Each one has an idBoard property. Bingo!

Generating the site

The plan is to fetch the data from Trello and apply it to some templates to populate our site. Most static site generators (SSG) would do the job. That’s what they are good at. I’ll use Eleventy because I think it has the simplest concepts to understand. Plus, it is very efficient at getting data and generating clean HTML with Nunjucks (a popular templating language).

We’ll want to be able to use an expression lin our template that outputs a section element for each item found in a JavaScript object called trello:

<!-- index.njk --> {% for card in trello %} <section>   <h2>{{ card.name }}</h2>   <div>     {% markdown %}       {{- card.desc | safe }}     {% endmarkdown %}   </div> </section> {% endfor %}

Fetching the data for the build

A popular technique with Jamstack sites like this is to run a build with Gulp, Grunt or which goes and fetches data from various APIs and feeds, stashes the data in a suitable format for the SSG, and then runs the SSG to generate the HTML. This works rather nicely.

Eleventy simplifies things here by supporting the execution of JavaScript in its data files. In other words, rather than only leveraging data stored as JSON or YAML, it can use whatever gets returned by JavaScript, opening the door to making requests directly to APIs when the Eleventy build runs. We won’t need a separate build step to go off to fetch data first. Eleventy will do it for us.

Let’s use that to get the data for our trello object in the templates.

We could use the Trello Node client to query the API, but as it turns out all the data we want is right there in the JSON for the board. Everything! In one request! We can just fetch it in one go!

// trello.js module.exports = () => {   const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json';    // Use node-fetch to get the JSON data about this board   const fetch = require('node-fetch');   return fetch(TRELLO_JSON_URL)     .then(res => res.json())     .then(json => console.log(json)); };

However, we don’t want to show all the data from that board. It includes cards on other lists, cards which have been closed and deleted, and so on. But we can filter the cards to only include the ones of interest thanks to JavaScript’s filter method.

// trello.js module.exports = () => {    const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json'    const TRELLO_LIST_ID='5e98325d6d6bd120f2b7395f',      // Use node-fetch to get the JSON data about this board    const fetch = require('node-fetch');    return fetch(TRELLO_JSON_URL)    .then(res => res.json())    .then(json => {        // Just focus on the cards which are in the list we want      // and do not have a closed status      let contentCards = json.cards.filter(card => {        return card.idList == TRELLO_LIST_ID && !card.closed;      });        return contentCards;  }); };

That’ll do it! With this saved in a file called trello.js in Eleventy’s data directory, we’ll have this data ready to use in our templates in an object called trello

Done-zo! 🎉

But we can do better. Let’s also handle attached images, and also add a way to have content staged for review before it goes live.

Image attachments

It’s possible to attach files to cards in Trello. When you attach an image, it shows up right there in the card with the source URL of the asset described in the data. We can make use of that!

If a card has an image attachment, we’ll want to get its source URL, and add it as an image tag to what our template inserts into the page at build time. That means adding the Markdown for an image to the Markdown in the description property of our JSON (card.desc). 

Then we can let Eleventy turn that into HTML for us along with everything else. This code looks for cards in our JSON and massages the data into the shape that we’ll need.

// trello.js  // If a card has an attachment, add it as an image  // in the description markdown contentCards.forEach(card => {   if(card.attachments.length) {     card.desc = card.desc + `n![$ {card.name}]($ {card.attachments[0].url} '$ {card.name}')`;   } });

Now we can move images around in our content too. Handy!

Staging content

Let’s add one more flourish to how we can use Trello to manage our site’s content.

There are a few ways that we might want to preview content before launching it to the world. Our Trello board could have one list for staging and one list for production content. But that would make it hard to visualize how new content lives alongside that which is already published.

A better idea would be to use Trello’s labels to signify which cards are published live, and which should only be included on a staged version of the site. This will give us a nice workflow. We can add more content by adding a new card in the right place. Label it with “stage” and filter it out from the cards appearing on our production branch. 

Screenshot of the Trello board with a bright pink background. It has cards in a column called Published.
Label hints in Trello showing what content is staged and what is live

A little more filtering of our JavaScript object is called for:

// trello.js  // only include cards labelled with "live" or with // the name of the branch we are in contentCards = contentCards.filter(card => {   return card.labels.filter(label => (     label.name.toLowerCase() == 'live' ||     label.name.toLowerCase() == BRANCH    )).length;  });

We want the content labelled ‘live’ to show up on every version of the build, staging or not. In addition we’ll look to include cards which have a label matching a variable called “BRANCH”. 

How come? What’s that?

This is where we get crafty! I’ve chosen to host this site on Netlify (disclaimer: I work there). This means that I can run the build from Netlify’s CI/CD environment. This redeploys the site whenever I push changes to its git repository, and also gives access to a couple of other things which are really handy for this site. 

One is Branch deploys. If you want a new environment for a site, you can create one by making a new branch in the Git repository. The build will run in that context, and your site will be published on a subdomain which includes the branch name. Like this.

Take a look and you’ll see all the cards from our list, including the one which has the orange “stage” label. We included it in this build because its label matched the branch name for the build context. BRANCH was an environment variable which contained whichever branch the build ran in.

label.name.toLowerCase() == BRANCH

In theory, we could make as many branches and labels as we like, and have all sorts of staging and testing environments. Ready to promote something from “stage” to “live”? Swap the labels and you’re good to go!

But how does it update though?

The second perk we get from running the site build in a CI/CD such as Netlify’s is that we can trigger a build to run whenever we like. Netlify lets us create build hooks. These are webhooks which initiate a new deployment when you send an HTTP POST to them.

If Trello supports webhooks too, then we could stitch these services together and refresh the site automatically whenever the Trello board changes. And guess what… they do! Hoorah!

To create a Netlify build hook, you’ll need to visit your site’s admin panel. (You can bootstrap this demo site into a new Netlify site in a couple of clicks if you want to try it out.)

Screenshot of the netlify build hooks screen with options to add a build hook and generate a public deploy key.
Creating a Netlify Build hook

Now, armed with a new build hook URL, we’ll need to register a new Trello webhook which calls it when content changes. The method for creating webhooks in Trello is via Trello’s API

The repo for this site includes a little utility to call the Trello API and create the webhook for you. But you’ll need to have a Trello developer token and key. Thankfully, it is easy to create those for free by visiting the Trello Developer portal and following the instructions under “Authorizing a client.”

Got ‘em? Great! If you save them in a .env file in your project, you can run this command to set up the Trello webhook:

npm run hook --url https://api.netlify.com/build_hooks/XXXXX

And with that, we’ve created a nice little flow for managing content on a simple site. We can craft our frontend just the way we want it, and have updates to the content happen on a Trello board which automatically updates the site whenever changes are made.

Could I really use this though?

This is a simplistic example. That’s by design. I really wanted to demonstrate the concepts of decoupling, and of using the API of an external service to drive the content for a site.

This won’t replace a full-featured decoupled CMS for more involved projects. But the principles are totally applicable to more complex sites.

This model, however, could be a great match for the types of websites we see for businesses such as independent shops, bars and restaurants. Imagine a Trello board that has one list for managing a restaurant’s home page, and one for managing their menu items. Very approachable for the restaurant staff to manage, and far nicer than uploading a new PDF of the menu whenever it changes.

Ready to explore an example and experiment with your own board and content? Try this:


The post Using Trello as a Super Simple CMS appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

The various types of illustrations a designer can create

[Top]

Apple declined to implement 16 Web APIs in Safari due to privacy concerns

Why? Fingerprinting. Rather than these APIs being used for what they are meant for, they end up being used for gross ad tech. As in, “hey, we don’t know exactly who you are, but wait, through a script we can tell your phone stopped being idle from 8:00 am to 8:13 am and were near the Bluetooth device JBL BATHROOM, so it’s probably dad taking his morning poop! Let’s show him some ads for nicer speakers and flannel shirts ASAP.”

I’ll pull the complete list here from Catalin Cimpanu’s article:

  • Web Bluetooth – Allows websites to connect to nearby Bluetooth LE devices.
  • Web MIDI API – Allows websites to enumerate, manipulate and access MIDI devices.
  • Magnetometer API – Allows websites to access data about the local magnetic field around a user, as detected by the device’s primary magnetometer sensor.
  • Web NFC API – Allows websites to communicate with NFC tags through a device’s NFC reader.
  • Device Memory API – Allows websites to receive the approximate amount of device memory in gigabytes.
  • Network Information API – Provides information about the connection a device is using to communicate with the network and provides a means for scripts to be notified if the connection type changes
  • Battery Status API – Allows websites to receive information about the battery status of the hosting device.
  • Web Bluetooth Scanning – Allows websites to scan for nearby Bluetooth LE devices.
  • Ambient Light Sensor – Lets websites get the current light level or illuminance of the ambient light around the hosting device via the device’s native sensors.
  • HDCP Policy Check extension for EME – Allows websites to check for HDCP policies, used in media streaming/playback.
  • Proximity Sensor – Allows websites to retrieve data about the distance between a device and an object, as measured by a proximity sensor.
  • WebHID – Allows websites to retrieve information about locally connected Human Interface Device (HID) devices.
  • Serial API – Allows websites to write and read data from serial interfaces, used by devices such as microcontrollers, 3D printers, and othes.
  • Web USB – Lets websites communicate with devices via USB (Universal Serial Bus).
  • Geolocation Sensor (background geolocation) – A more modern version of the older Geolocation API that lets websites access geolocation data.
  • User Idle Detection – Lets website know when a user is idle.

I’m of mixing feelings. I do like the idea of the web being a competitive platform for building any sort of app and sometimes fancy APIs like this open those doors.

Not to mention that some of these APIs are designed to do responsible things, like knowing connections speeds through the Network Information API and sending less data if you can, and the same for the Battery Status API.

This is all a similar situation to :visited in CSS. Have you ever noticed how there are some CSS declarations you can’t use on visited links? JavaScript APIs will even literally lie about the current styling of visited links to make links always appear unvisited. Because fingerprinting.

Direct Link to ArticlePermalink


The post Apple declined to implement 16 Web APIs in Safari due to privacy concerns appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , ,
[Top]