Archiwum kategorii: CSS

Some Typography Links

Post pobrano z: Some Typography Links

I just can’t stop bookmarking great links related to typography. I’m afraid I’m going to have to subject you, yet again, to a bunch of them all grouped up. So those of you that care about web type stuff, enjoy.


I know there are lots of good reasons to be excited about variable fonts. The design possibilities of endless variations in one file is chief among them. But I remain the most excited about the performance benefits. Having a single file that elegantly handles the thicker weights (for bolding) and italics is so cool. I can’t wait to wave my fist saying back in my day we had to load multiple files for our font variations!

Mandy Michael digs into the performance implications in a deeper way than just reducing the number of requests.

Even if you consider the slightly larger file sizes, when combined with improved font compression formats like WOFF2, font subsetting and font loading techniques like font-display: swap; we end up in a situation where we can still get smaller overall font file sizes as well as a significant increase in stylistic opportunity.


Anna Monus did some variable font performance testing as well, evaluating the extreme case of loading 12 variations of a font against a variable font. Even though the copy of the variable font she had for Roboto was massive (over 1MB), there was a perf gain compared against loading 12 variations.


Roboto is on Google Fonts, of course, and it’s got the #1 position by far. But Google Fonts has Inter now, and I’d expect that leap up in the charts as it’s got a style that everyone seems to like and can work with just about anything.


Seeing a variable font control a smiley face is never not gonna make me happy. Don’t miss that first face-ness slider, lollllz.


Klint Finley on the proliferation of high-quality open-source fonts for WIRED. Sometimes they are backed by companies with thick wallets, which makes sense. But the motivation for doing it varies. Sometimes quality is the goal. Like open-source anything, lots of contributions, can, if handled well, lead to a better product. But open-source doesn’t always mean there isn’t a business possibility, and if not, not everyone cares about turning a profit on everything.


Speaking of open-source fonts, Collletttivo is an open-source font foundry with a good dozen typefaces, including a variable font one. It’s a super fun site to explore with the little fake windows you open up and move around.


There’s a new mac app called FontGoggles for poking into a font file and taking a look at what it offers. Seems like that would be easy but it really isn’t. I like that it supports WOFF/2 as that’s pretty much all we deal with on the web.

Also, remember there is a website that looks around in font files with the best name ever.


Font Match allows you to put fonts on top of each other for comparisons. Seems like it’s more for large type and comparing their features, while Font style matcher is more about comparing paragraph text.

In fact, using Font style matcher to make a perfect font fallback is one of my all-time favorite CSS-Tricks. Sssshh… don’t tell anybody but I’m compiling those all-time favorites into a book, like this chapter. You’d have to subscribe to read it, because that’s kind of the point: I’d like to sell the book. So if you happen to subscribe now, there is stuff to read there, but you’d be a very early supporter for the rest of it.

The post Some Typography Links appeared first on CSS-Tricks.

Getting JavaScript to Talk to CSS and Sass

Post pobrano z: Getting JavaScript to Talk to CSS and Sass

JavaScript and CSS have lived beside one another for upwards of 20 years. And yet it’s been remarkably tough to share data between them. There have been large attempts, sure. But, I have something simple and intuitive in mind — something not involving a structural change, but rather putting CSS custom properties and even Sass variables to use.

CSS custom properties and JavaScript

Custom properties shouldn’t be all that surprising here. One thing they’ve always been able to do since browsers started supporting them is work alongside JavaScript to set and manipulate the values.

Specifically, though, we can use JavaScript with custom properties in a few ways. We can set the value of a custom property using setProperty:

document.documentElement.style.setProperty("--padding", 124 + "px"); // 124px

We can also retrieve CSS variables using getComputedStyle in JavaScript. The logic behind this is fairly simple: custom properties are part of the style, therefore, they are part of computed style.

getComputedStyle(document.documentElement).getPropertyValue('--padding') // 124px

Same sort of deal with getPropertyValue. That let us get the custom property value from an inlined style from HTML markup.

document.documentElement.style.getPropertyValue("--padding'"); // 124px

Note that custom properties are scoped. This means we need to get computed styles from a particular element. As we previously defined our variable in :root we get them on the HTML element.

Sass variables and JavaScript

Sass is a pre-processing language, meaning it’s turned into CSS before it ever is a part of a website. For that reason, accessing them from JavaScript in the same way as CSS custom properties — which are accessible in the DOM as computed styles — is not possible. 

We need to modify our build process to change this. I doubt there isn’t a huge need for this in most cases since loaders are often already part of a build process. But if that’s not the case in your project, we need three modules that are capable of importing and translating Sass modules.

Here’s how that looks in a webpack configuration:

module.exports = {
 // ...
 module: {
  rules: [
   {
    test: /\.scss$/,
    use: ["style-loader", "css-loader", "sass-loader"]
   },
   // ...
  ]
 }
};

To make Sass (or, specifically, SCSS in this case) variables available to JavaScript, we need to “export” them.

// variables.scss
$primary-color: #fe4e5e;
$background-color: #fefefe;
$padding: 124px;

:export {
  primaryColor: $primary-color;
  backgroundColor: $background-color;
  padding: $padding;
}

The :export block is the magic sauce webpack uses to import the variables. What is nice about this approach is that we can rename the variables using camelCase syntax and choose what we expose.

Then we import the Sass file (variables.scss) file into JavaScript, giving us access to the variables defined in the file.

import variables from './variables.scss';

/*
 {
  primaryColor: "#fe4e5e"
  backgroundColor: "#fefefe"
  padding: "124px"
 }
*/

document.getElementById("app").style.padding = variables.padding;

There are some restrictions on the :export syntax that are worth calling out:

  • It must be at the top level but can be anywhere in the file.
  • If there is more than one in a file, the keys and values are combined and exported together.
  • If a particular exportedKey is duplicated, the last one (in the source order) takes precedence.
  • An exportedValue may contain any character that’s valid in CSS declaration values (including spaces).
  • An exportedValue does not need to be quoted because it is already treated as a literal string.

There are lots of ways having access to Sass variables in JavaScript can come in handy. I tend to reach for this approach for sharing breakpoints. Here is my breakpoints.scs file, which I later import in JavaScript so I can use the matchMedia() method to have consistent breakpoints.

// Sass variables that define breakpoint values
$breakpoints: (
  mobile: 375px,
  tablet: 768px,
  // etc.
);

// Sass variables for writing out media queries
$media: (
  mobile: '(max-width: #{map-get($breakpoints, mobile)})',
  tablet: '(max-width: #{map-get($breakpoints, tablet)})',
  // etc.
);

// The export module that makes Sass variables accessible in JavaScript
:export {
  breakpointMobile: unquote(map-get($media, mobile));
  breakpointTablet: unquote(map-get($media, tablet));
  // etc.
}

Animations are another use case. The duration of an animation is usually stored in CSS, but more complex animations need to be done with JavaScript’s help.

// animation.scss
$global-animation-duration: 300ms;
$global-animation-easing: ease-in-out;

:export {
  animationDuration: strip-unit($global-animation-duration);
  animationEasing: $global-animation-easing;
}

Notice that I use a custom strip-unit function when exporting the variable. This allows me to easily parse things on the JavaScript side.

// main.js
document.getElementById('image').animate([
  { transform: 'scale(1)', opacity: 1, offset: 0 },
  { transform: 'scale(.6)', opacity: .6, offset: 1 }
], {
  duration: Number(variables.animationDuration),
  easing: variables.animationEasing,
});

It makes me happy that I can exchange data between CSS, Sass and JavaScript so easily. Sharing variables like this makes code simple and DRY.

There are multiple ways to achieve the same sort of thing, of course. Les James shared an interesting approach in 2017 that allows Sass and JavaScript to interact via JSON. I may be biased, but I find the approach we covered here to be the simplest and most intuitive. It doesn’t require crazy changes to the way you already use and write CSS and JavaScript.

Are there other approaches that you might be using somewhere? Share them here in the comments — I’d love to see how you’re solving it.

The post Getting JavaScript to Talk to CSS and Sass appeared first on CSS-Tricks.

Google’s Technical Writing Guide

Post pobrano z: Google’s Technical Writing Guide

It’s good!

I’ve written up my advice (sprinkled with great advice from others), but this is way more straightforward nuts-and-bolts training on technical writing. It’s structured like an actual course, with exercises along the way.

I’m far from an expert here. But between Geoff and I, we end up doing a lot of technical article editing for the sake of clarity.

Comedy writers seek the funniest results, horror writers strive for the scariest, and technical writers aim for the clearest. In technical writing, clarity takes precedence over all other rules. 

It can be tricky to get right. Read the section on Active voice. That’s easy for anyone to get wrong.

Essentially every single rule is just an extension of “make it more clear.”

Direct Link to ArticlePermalink

The post Google’s Technical Writing Guide appeared first on CSS-Tricks.

Accessibility Links

Post pobrano z: Accessibility Links

Austin Gil has kicked off the first in a five-part series about “HTML Forms Right” and to starts with semantics. It’s talking to the “we build our front-ends with JavaScript” crowd. The first block of code is an example of an Ajax form submission where the data submitted is gathered through the JavaScript API FormData.

Why is that so vital? Well, no <form> tag, no FormData. Why else use a form (aside from the Enter-key submission):

“But Austin, I’m building an SPA. Therefore if the user even sees the form, it means JavaScript MUST be enabled.” And you’d be right. Although, if it is an important form, you may want to consider supporting a no-JS world. The day may come that you want to implement SSR.

Server-Side Rendering (SSR) is going to get easier and easier to do as the benefits of it become more and more obvious. Google tells us a page that is client-side rendered has week-long-ish queue to get indexed and re-indexed on changes. Not to mention SSR is almost definitely going to be far faster to load.


Oscar Braunert’s Inclusive Inputs is a nice follow-up read as it begins with form HTML that is so close to being right, but is painfully not right. (Hint: it’s missing the label/input connection). Then he gets into interesting patterns like how to accessibly mark up required fields and fields with errors. Like:

<div class="form-group">
  <label for="password">
    Password
    <span class="required" aria-hidden="true">*</span>
    <span class="sr-only">required</span>
  </label>
  <input 
    type="password"
    id="password"
    name="password"
    aria-describedby="desc_pw"
  >
  <p class="aside" id="desc_pw">Your password needs to have at least eight characters.</p>
</div>

Amber Wilson gets into Accessible HTML elements with the twist of avoiding any ARIA usage at all:

You may be aware that ARIA roles are often used with HTML elements. I haven’t written about them here, as it’s good to see how HTML written without ARIA can still be accessible.

Shout out to <dl>.


Sarah Higley does get into ARIA in Roles and relationships, but she warns us to be very careful upfront:

[…] a budding accessibility practitioner might find themselves experimenting with more serious roles like menulistbox, or even treegrid. These are tantalizing, powerful patterns that allow you to create experiences that are not supported by only vanilla HTML. Unfortunately, they are also brittle; even small mistakes in using these roles can take a user on a very bad trip.

Talk to your kids about ARIA before it’s too late.

Ideally, don’t use ARIA at all. But if the accessibility is screwed up to the point it can’t be fixed at the DOM level, Sarah gets into some tricks. For example, one uses role="presentation" to essentially remove an element’s default role (when it is in the way).


Speaking of ARIA and not using it unless you have to, one of the things you can do with ARIA is label controls. Adrian Roselli has thoughts on how best to do that:

Here is the priority I follow when assigning an accessible name to a control:

1. Native HTML techniques
2. aria-labelledby pointing at existing visible text
3. Visibly-hidden content that is still in the page
4. aria-label

The post Accessibility Links appeared first on CSS-Tricks.

How to use CSS Scroll Snap

Post pobrano z: How to use CSS Scroll Snap

Nada Rifki demonstrates the scroll-snap-type and scroll-snap-alignCSS properties. I like that the demo shows that the items in the scrolling container can be different sizes. It is the edges of those children that matter, not some fixed snapping distance.

I like Max Kohler’s coverage as well, which includes a demo where the snapping can happen in multiple directions.

This is one of those things where, if you didn’t know about it, it’s worth a solid golf clap for CSS.

Direct Link to ArticlePermalink

The post How to use CSS Scroll Snap appeared first on CSS-Tricks.

Emergency Website Kit

Post pobrano z: Emergency Website Kit

Here’s an outstanding idea from Max Böck. He’s created a boilerplate project for building websites that fit within a single HTTP request. This is extremely important for websites that contain critical information for public safety. As Max writes:

In cases of emergency, many organizations need a quick way to publish critical information. But exisiting (CMS) websites are often unable to handle sudden spikes in traffic.

What’s so special about this boilerplate? Well, it does smart stuff like:

  • generates a static site using Eleventy,
  • uses minimal markup with inlined CSS,
  • aims to transmit everything in the first connection roundtrip (~14KB),
  • progressively enables offline-support with Service Workers,
  • uses Netlify CMS for easy content editing, and
  • provides one-click deployment via Netlify to get off the ground quickly

The example website that Max built with this boilerplate is shockingly fast and I would go one step further to argue that all websites should feel as fast as this, not just websites that are useful in an emergency.

Direct Link to ArticlePermalink

The post Emergency Website Kit appeared first on CSS-Tricks.

Creating an Editable Site with Google Sheets and Eleventy

Post pobrano z: Creating an Editable Site with Google Sheets and Eleventy

Remember Tabletop.js? We just covered it a little bit ago in this same exact context: building editable websites. It’s a tool that turns a Google Sheet into an API, that you as a developer can hit for data when building a website. In that last article, we used that API on the client side, meaning JavaScript needed to run on every single page view, hit that URL for the data, and build the page. That might be OK in some circumstances, but let’s do it one better. Let’s hit the API during the build step so that the content is built into the HTML directly. This will be far faster and more resilient.

The situation

As a developer, you might have had to work with clients who keep bugging you with unending revisions on content, sometimes, even after months of building the site. That can be frustrating as it keeps pulling you back, preventing you from doing more productive work.

We’re going to give them the keys to updating content themselves using a tool they are probably already familiar with: Google Sheets.

A new tool

In the last article, we introduced the concept of using Google Sheets with Tabletop.js. Now let’s introduce a new tool to this party: Eleventy

We’ll be using Eleventy (a static site generator) because we want the site to be rendered as a pure static site without having to ship all of the under workings of the site in the client side JavaScript. We’ll be pulling the content from the API at build time and having Eleventy create a minified index.html that we’ll push to the server for the production website. By being static, this allows the page to load faster and is better for security reasons.

The spreadsheet

We’ll be using a demo I built, with its repo and Google Sheet to demonstrate how to replicate something similar in your own projects. First, we’ll need a Google Sheet which will be our data store.

Open a new spreadsheet and enter your own values in the columns just like mine. The first cell of each column is the reference that’ll be used later in our JavaScript, and the second cell is the actual content that gets displayed.

In the first column, “header” is the reference name and “Please edit me!” is the actual content in the first column.

Next up, we’ll publish the data to the web by clicking on File → Publish to the web in the menu bar.

A link will be provided, but it’s technically useless to us, so we can ignore it. The important thing is that the spreadsheet(and its data) is now publicly accessible so we can fetch it for our app.

Take note that we’ll need the unique ID of the sheet from its URL  as we go on.

Node is required to continue, so be sure that’s installed. If you want to cut through the process of installing all of thedependencies for this work, you can fork or download my repo and run:

npm install

Run this command next — I’ll explain why it’s important in a bit:

npm run seed

Then to run it locally:

npm run dev

Alright, let’s go into src/site/_data/prod/sheet.js. This is where we’re going to pull in data from the GoogleSheet, then turn it into an object we can easily use, and finally convert the JavaScript object back to JSON format. The JSON is stored locally for development so we don’t need to hit the API every time.

Here’s the code we want in there. Again, be sure to change the variable sheetID to the unique ID of your own sheet.


module.exports = () => {
  return new Promise((resolve, reject) => {
    console.log(`Requesting content from ${googleSheetUrl}`);
    axios.get(googleSheetUrl)
      .then(response => {
        // massage the data from the Google Sheets API into
        // a shape that will more convenient for us in our SSG.
        var data = {
          "content": []
        };
        response.data.feed.entry.forEach(item => {
          data.content.push({
            "header": item.gsx$header.$t,
            "header2": item.gsx$header2.$t,
            "body": item.gsx$body.$t,
            "body2": item.gsx$body2.$t,
            "body3":  item.gsx$body3.$t,
            "body4": item.gsx$body4.$t,
            "body5": item.gsx$body5.$t,
            "body6":  item.gsx$body6.$t,
            "body7": item.gsx$body7.$t,
            "body8": item.gsx$body8.$t,
            "body9":  item.gsx$body9.$t,
            "body10": item.gsx$body10.$t,
            "body11": item.gsx$body11.$t,
            "body12":  item.gsx$body12.$t,
            "body13": item.gsx$body13.$t,
            "body14": item.gsx$body14.$t,
            "body15":  item.gsx$body15.$t,
            "body16": item.gsx$body16.$t,
            "body17": item.gsx$body17.$t,
            
          })
        });
        // stash the data locally for developing without
        // needing to hit the API each time.
        seed(JSON.stringify(data), `${__dirname}/../dev/sheet.json`);
        // resolve the promise and return the data
        resolve(data);
      })
      // uh-oh. Handle any errrors we might encounter
      .catch(error => {
        console.log('Error :', error);
        reject(error);
      });
  })
}

In module.exports, there’s a promise that’ll resolve our data or throw errors when necessary. You’ll notice that I’m using a axios to fetch the data from the spreadsheet. I like the it handles status error codes by rejecting the promise automatically, unlike something like Fetch that requires monitoring error codes manually.

I created a data object in there with a content array in it. Feel free to change the structure of the object, depending on what the spreadsheet looks like.

We’re using the forEach() method to loop through each spreadsheet column while equating it with the corresponding name we want to allocate to it, while pushing all of these into the data object as content. 

Remember that seed command from earlier? We’re using seed to transform what’s in the data object to JSON by way of JSON.stringify, which is then sent to src/site/_data/dev/sheet.json

Yes! Now have data in a format we can use with any templating engine, like Nunjucks, to manipulate it. But, we’re focusing on content in this project, so we’ll be using the index.md template format to communicate the data stored in the project.

For example, here’s how it looks to pull item.header through a for loop statement:

<div class="listing">
{%- for item in sheet.content -%}
  <h1>{{ item.header }} </h1>
{%- endfor -%}
</div>

If you’re using Nunjucks, or any other templating engine, you’ll have to pull the data accordingly.

Finally, let’s build this out:

npm run build

Note that you’ll want a dist folder in the project where the build process can send the compiled assets.

But that’s not all! If we were to edit the Google Sheet, we won’t see anything update on our site. That’s where Zapier comes in. We can “zap” Google sheet and Netlify so that an update to the Google Sheet triggers a deployment from Netlify.

Assuming you have a Zapier account up and running, we can create the zap by granting permissions for Google and Netlify to talk to one another, then adding triggers.

The recipe we’re looking for? We’re connecting Google Sheets to Netlify so that when a “new or updated sheet row” takes place, Netlify starts a deploy. It’s truly a set-it-and-forget-it sort of deal.

Yay, there we go! We have a performant static site that takes its data from Google Sheets and deploys automatically when updates are made to the sheet.

The post Creating an Editable Site with Google Sheets and Eleventy appeared first on CSS-Tricks.

Maintaining Performance

Post pobrano z: Maintaining Performance

Real talk from Dave:

I, Dave Rupert, a person who cares about web performance, a person who reads web performance blogs, a person who spends lots of hours trying to keep up on best practices, a person who co-hosts a weekly podcast about making websites and speak with web performance professionals… somehow goofed and added 33 SECONDS to their page load.

This stuff is hard even when you care a lot. The 33 seconds came from font preloading rather than the one-line wonder of font-display.

I also care about making fast websites, but mine aren’t winning any speed awards because I’ll take practical and maintainable over peak performance any day. (Sorry, world)

Direct Link to ArticlePermalink

The post Maintaining Performance appeared first on CSS-Tricks.

Auto-Archival

Post pobrano z: Auto-Archival

I’m sure most of us have used the ol’ Wayback Machine to access some site that’s gone offline. I don’t actually know how it decides what sites to archive and when, but you can tell it to save pages. There is UI for it right on its homepage.

Also, there is a little trick…

Typing https://t.co/R5w2bQZKWz in front of any URL saves that content in the Wayback Machine forever. Nasty tweet? Type https://t.co/R5w2bQZKWz in front of the URL, and archive it forever. Hat tip: @t.

— zeldman (@zeldman) February 23, 2019

That’s still a bit manual though.

Brian Kardell was given access to some kind of secret API that allows submission of pages, and he built a public service around it anyone can use. Here’s his blog post on it. You hit the endpoint with some JSON in your choice of a couple of formats and it’ll do the rest. The idea is that other systems would use this for submissions. Imagine a WordPress plugin that hit it when you hit submit or update on a post. Or a Netlify build plugin that pinged this as you deployed.

I’m not entirely sure what the difference is between this service and the URL technique from Zeldman’s tweet, but I gotta imagine an API-based submission service is more reliable.

The big idea is that you’re telling this service to archive your page forever, which is the mission of the Internet Archive. So, should your site ever go away, the content lives on. So you’d better want that before you do this!

The post Auto-Archival appeared first on CSS-Tricks.

Client-Side Image Editing on Mobile

Post pobrano z: Client-Side Image Editing on Mobile

Michael Scharnagl:

Ever wanted to easily convert an image to a grayscale image on your phone? I do sometimes, and that’s why I build a demo using the Web Share Target API to achieve exactly that.

For this I used the Service Worker way to handle the data. Once the data is received on the client, I use drawImage from canvas to draw the image in canvas, use the grayscale filter to convert it to a grayscale image and output the final image.

So you „install” the little microsite like a PWA, then you natively „share” an image to it and it comes back edited. Clever. Android on Chrome only at the moment.

Reminds me of this „Browser Functions” idea in reverse. That was a server that did things a browser can do, this is a browser doing things a server normally does.

Direct Link to ArticlePermalink

The post Client-Side Image Editing on Mobile appeared first on CSS-Tricks.