You are here

CSS Tricks

Subscribe to CSS Tricks feed
Tips, Tricks, and Techniques on using Cascading Style Sheets.
Updated: 25 min 53 sec ago

UX Considerations for Web Sharing

8 hours 29 min ago

From trashy clickbait sites to the most august of publications, share buttons have long been ubiquitous across the web. And yet it is arguable that these buttons aren’t needed. All mobile browsers — Firefox, Edge, Safari, Chrome, Opera Mini, UC Browser, Samsung Internet — make it easy to share content directly from their native platforms. They all feature a built-in button to bring up a "share sheet" — a native dialog for sharing content. You can also highlight text to share an excerpt along with the link.

The ubiquitous share button, as seen at the BBC, Wired, BuzzFeed, PBS, The Wall Street Journal and elsewhere.

Given that users can share by default, are custom buttons taking up unnecessary space and potentially distracting users from more important actions? And do people actually use them?

A (unscientific) poll of 12,500 CSS-Tricks readers found that 60% of its readers never used custom share buttons. That was in 2014 and native methods for sharing have only improved since then. A more recent poll from Smashing Magazine found much the same thing.

How often do you use social sharing buttons on your mobile device?

— Smashing Magazine (@smashingmag) August 23, 2019

Users come with varying technological proficiency. Not everybody knows their way around there own mobile phone or browser meaning some users would struggle to find the native share button. It’s also worth thinking about what happens on desktop. Desktop browsers generally (with Safari as one exception) offer no built-in sharing functionality — users are left to copy and paste the link into their social network of choice.

Some data suggests that clickthrough rates are relatively low. However, clickthrough rates aren’t the best metric. For technically savvy users aware of how to share without assistance, the buttons can still act as a prompt — a visual reminder to share the content. Regardless of how the link is ultimately shared, a custom share button can still provide a cue, or a little nudge to elicit the share. For this reason, measuring click rates on the buttons themselves may not be entirely fair — people may see the button, feel encouraged to share the content, but then use the browsers built-in share button. A better metric would be whether shares increase after the addition of share buttons, regardless of how they’re shared.

We’ve established that having a share button is probably useful. Websites have traditionally featured separate buttons for two or three of the most popular social networks. With a new-ish API, we can do better. While browser support is currently limited to Chrome for Android and Safari, those two browsers alone make up the vast majority of web traffic.

The Web Share API

The Web Share API offers a simple way to bring up a share sheet — the native bit of UI that’s used for sharing. Rather than offering a standard list of popular social networks to share to (which the user may or may not be a member of), the options of the share sheet are catered to the individual. Only applications they have installed on their phone will be shown. Rather than a uniform list, the user will be shown only the option to share on networks they actually use — whether that be Twitter and Facebook or something more esoteric.

Not showing the user irrelevant networks is obviously a good thing. Unfortunately, this is counterbalanced by the fact that some users visit social media websites rather than downloading them as apps. If you use, for example, but haven’t installed the Twitter application natively, then Twitter will not be listed as a sharing option. Currently, only native apps are listed but PWAs will be supported in the future.

websharebutton.addEventListener("click", function() { navigator.share({ url: document.URL, title: document.title, text: "lorem ipsum..." }); });

The API requires user interaction (such as a button click as shown in the above code) to bring up the share sheet. This means you can’t do something obnoxious like bring up the share sheet on page load.

The text might be a short excerpt or a summation of the page. The title is used when sharing via email but will be ignored when sharing to social networks.

Native sharesheet dialog on Android (left) and iOS (right). The apps listed here are dependent on which apps you have installed on your device. Sharing on desktop

While we are pretty happy with the Web Share API for mobile, its implementation for desktop is currently limited to Safari and leaves a lot to be desired. (Chrome is planning to ship support eventually, but there is no clear timescale).

The provided options — Mail, Message, Airdrop, Notes, Reminders — omit social networks. Native apps for Twitter and Facebook are common on phones, but rare on other devices.

Instead of relying on the Web Share API for desktop, its relatively common to have a generic share button that opens a modal that offers more multiple sharing options. This is the approach adopted by YouTube, Instagram and Pinterest, among others.

Instagram (left) compared to YouTube (right)

Facebook and Twitter account for the vast majority of sharing online, so offering an exhaustive list of social networks to choose from doesn’t feel necessary. (An option for Instagram would be ideal, but it is currently not technically possible to share to Instagram from a website.) It is also relatively common to include an email option. For anyone using a web-based email client like or rather than the operating system’s default email application, this is problematic.

Many people make use of web-based email client’s or A share-by-email button will open the operating system’s default email application. Users will be greeted by a prompt to set this up, which is far more effort than simply copy and pasting the URL. It is therefore advisable to omit the email option and instead include a button to copy the link to the clipboard, which is only infinitesimally easier than doing a copy in the address bar with the keyboard.

A prompt to set up the default email application on Mac

Prompting the user to set up an application they never use is far more effort than simply copy and pasting a URL into my preferred email client.

Choosing a share icon There have been plenty of other share icons over the years.

There is no universal standardized share icon — far from it. While the Android symbol might not be recognizable to long-term iPhone users, the iOS icon is problematic. It is identical to the download icon — but with the arrow in the opposite direction, which would imply uploading, not sharing.

Where I work at giffgaff, we polled 69 of our colleagues on whether they recognized the current iOS icon or the current Android icon as representing sharing. The Android icon was an overwhelming winner with 58 votes. While our sample did include iPhone users, some long-term iPhone users may not be familiar with this symbol (even though it has been adopted by some websites). Then there is the forward arrow, an icon that was abandoned by Apple, but adopted elsewhere. Reminiscent of the icon for forwarding an email, this symbol has been made recognizable by its usage on The icon was adopted by Microsoft in 2017 after A/B testing found a high level of familiarity.

It’s also possible to take a contextual approach. Twitter will change the icon used depending on the platform of the user. This approach is also taken by the icon library Ionicons.

Android (left) and Mac/iOS (right)

Given the lack of a universally understood icon, this seems like a good approach. Alternatively, make sure to include a label alongside the icon to really spell things out to the user.

The post UX Considerations for Web Sharing appeared first on CSS-Tricks.

Categories: Design

Table with Expando Rows

8 hours 29 min ago

"Expando Rows" is a concept where multiple related rows in a <table> are collapsed until you open them. You'd call that "progressive disclosure" in interaction design parlance.

After all these years on CSS-Tricks, I have a little better eye for what the accessibility concerns of a design/interactivity feature are. I'm not entirely sure how I would have approached this problem myself, but there is a good chance that whatever I would have tried wouldn't have hit the bullseye with accessibility.

That's why I'm extra happy when someone like Adrian Roselli tackles problems like this, because the accessibility is handled right up front (see the videos in all the major screen readers).

I feel the same way when we get demos from Scott O'Hara, Lindsey Kopacz, and Hedyon Pickering.

See the Pen
Table with Expando Rows
by Adrian Roselli (@aardrian)
on CodePen.

Direct Link to ArticlePermalink

The post Table with Expando Rows appeared first on CSS-Tricks.

Categories: Design

Weekly Platform News: Emoji String Length, Issues with Rounded Buttons, Bundled Exchanges

Thu, 09/19/2019 - 11:29

In this week's roundup, the string length of two emojis is not always equal, something to consider before making that rounded button, and we may have a new way to share web apps between devices, even when they are offline.

The JavaScript string length of emoji characters

A single rendered emoji can have a JavaScript string length of up to 7 if it contains additional Unicode scalar values that represent a skin tone modifier, gender specification, and multicolor rendering.

(via Henri Sivonen)

An accessibility issue with rounded buttons

Be aware that applying CSS border-radius to a <button> element reduces the button’s interactive area (“those lost corner pixels are no longer clickable”).

You can avoid this accessibility issue in CSS, e.g., by emulating rounded corners via border-image instead, or by overlaying the button with an absolutely positioned, transparent ::before pseudo-element.

(via Tyler Sticka)

Sharing web pages while offline with Bundled Exchanges

Chrome plans to add support for navigation to Bundled Exchanges (part of Web Packaging). A bundled exchangeis a collection of HTTP request/response pairs, and it can be used to bundle a web page and all of its resources.

The browser should be able to parse and verify the bundle’s signature and then navigate to the website represented by the bundle without actually connecting to the site as all the necessary subresources could be served by the bundle.

Kinuko Yasuda from Google has posted a video that demonstrates how Bundled Exchanges enable sharing web pages (e.g., a web game) with other devices while offline.

(via Kinuko Yasuda)

Read even more news in my weekly Sunday issue, which can be delivered to you via email every Monday morning. Visit for more information.

The post Weekly Platform News: Emoji String Length, Issues with Rounded Buttons, Bundled Exchanges appeared first on CSS-Tricks.

Categories: Design

Buddy on CSS-Tricks

Thu, 09/19/2019 - 11:28

Here's a little direct product endorsement for ya: I literally use Buddy for deployment on all my projects.

Buddy isn't just a deployment tool, we'll get to that, but it's something that Buddy does very well and definitely a reason you might look at picking it up yourself if you're looking around for a reliable, high-quality deployment service.

Here's my current setup:

  • CSS-Tricks is WordPress site.
  • The whole wp-content folder is a private repository on GitHub.
  • The hosting is on Flywheel, which gives me SFTP access to the server.
  • When I push to the Master branch, Buddy automatically deploys the changed files over SFTP. This is fast because the fact it's only dealing with changed files.

The setup on Buddy for this is incredibly nice and simple and I've never once had any problems with it. You may want to look at zero-downtime deployments as well, where files are uploaded to a separate directory first and swapped out with the destination directories if the entire upload is successful.

And I don't just use this setup for CSS-Tricks but all my sites that need this kind of deployment.

But like I said, Buddy isn't just deployment. Buddy is all about pipelines. You (visually) configure a bunch of tasks that you want Buddy to do for you and the trigger that kicks it off. Pushing to Master is just one possible trigger, you can also kick them off manually or on a timer.

What tasks? Well, a common one would be running your tests. You know: Continuous Integration (CI) and Continuous Development (CD). You can tell Buddy to run whatever terminal commands you want (they'll spin up a Docker container for you), so however you run tests and get output will work just fine.

You could have it shoot you an email, hit some other web service, or run a build process.

Here's the actual tasks I run in my pipeline right now:

  1. Upload the files over SFTP
  2. Tell Cloudflare to purge all the cache on the site
  3. Send a message to a particular channel on Slack (also do that on failure)

So useful.

It's so easy to set up it almost encourages doing more with your pipelines. I need to get some Cypress tests in there and I'd love to integrate an action to automatically optimize all images in the commits.

The post Buddy on CSS-Tricks appeared first on CSS-Tricks.

Categories: Design

Git Pathspecs and How to Use Them

Thu, 09/19/2019 - 07:19

When I was looking through the documentation of git commands, I noticed that many of them had an option for <pathspec>. I initially thought that this was just a technical way to say “path,” and assumed that it could only accept directories and filenames. After diving into the rabbit hole of documentation, I found that the pathspec option of git commands are capable of so much more.

The pathspec is the mechanism that git uses for limiting the scope of a git command to a subset of the repository. If you have used much git, you have likely used a pathspec whether you know it or not. For example, in the command git add, the pathspec is However, it is capable of much more nuance and flexibility.

So, why should you learn about pathspecs? Since it is a part of many commands, these commands become much more powerful with an understanding of pathspecs. With git add, you can add just the files within a single directory. With git diff, you can examine just the changes made to filenames with an extension of .scss. You can git grep all files except for those in the /dist directory.

In addition, pathspecs can help with the writing of more generic git aliases. For example, I have an alias named git todo, which will search all of my repository files for the string 'todo'. However, I would like for this to show all instances of the string, even if they are not within my current working directory. With pathspecs, we will see how this becomes possible.

File or directory

The most straightforward way to use a pathspec is with just a directory and/or filename. For example, with git add you can do the following. ., src/, and README are the respective pathspecs for each command.

git add . # add CWD (current working directory) git add src/ # add src/ directory git add README # add only README directory

You can also add multiple pathspecs to a command:

git add src/ server/ # adds both src/ and server/ directories

Sometimes, you may see a -- preceding the pathspec of a command. This is used to remove any ambiguity of what is the pathspec and what is part of the command.


In addition to files & directories, you can match patterns using *, ?, and []. The * symbol is used as a wildcard and it will match the / in paths — in other words, it will search through subdirectories.

git log '*.js' # logs all .js files in CWD and subdirectories git log '.*' # logs all 'hidden' files and directories in CWD git log '*/.*' # logs all 'hidden' files and directories in subdirectories

The quotes are important, especially when using *! They prevent your shell (such as bash or ZSH) from attempting to expand the wildcards on their own. For example, let’s take a look at how git ls-files will list files with and without the quotes.

# example directory structure # # . # ├── package-lock.json # ├── package.json # └── data # ├── bar.json # ├── baz.json # └── foo.json git ls-files *.json # package-lock.json # package.json git ls-files '*.json' # data/bar.json # data/baz.json # data/foo.json # package-lock.json # package.json

Since the shell is expanding the * in the first command, git ls-files receives the command as git ls-files package-lock.json package.json. The quotes ensure that git is the one to resolve the wildcard.

You can also use the ? character as a wildcard for a single character. For example, to match either mp3 or mp4 files, you can do the following.

git ls-files '*.mp?' Bracket expressions

You can also use “bracket expressions” to match a single character out of a set. For example, if you'd like to make matches between either TypeScript or JavaScript files, you can use [tj]. This will match either a t or a j.

git ls-files '*.[tj]s'

This will match either .ts files or .js files. In addition to just using characters, there are certain collections of characters that can be referenced within bracket expressions. For example, you can use [:digit:] within a bracket expression to match any decimal digit, or you can use [:space:] to match any space characters.

git ls-files '*.mp[[:digit:]]' # mp0, mp1, mp2, mp3, ..., mp9 git ls-files '*[[:space:]]*' # matches any path containing a space

To read more about bracket expression and how to use them, check out the GNU manual.

Magic signatures

Pathspecs also have the special tool in their arsenal called “magic signatures” which unlock some additional functionality to your pathspecs. These “magic signatures” are called by using :(signature) at the beginning of your pathspec. If this doesn't make sense, don't worry: some examples will hopefully help clear it up.


The top signature tells git to match the pattern from the root of the git repository rather than the current working directory. You can also use the shorthand :/ rather than :(top).

git ls-files ':(top)*.js' git ls-files ':/*.js' # shorthand

This will list all files in your repository that have an extension of .js. With the top signature this can be called within any subdirectory in your repository. I find this to be especially useful when writing generic git aliases!

git config --global alias.js 'ls-files -- ':(top)*.js''

You can use git js anywhere within your repository to get a list of all JavaScript files in your project using this.


The icase signature tells git to not care about case when matching. This could be useful if you don't care which case the filename is — for example, this could be useful for matching jpg files, which sometimes use the uppercase extension JPG.

git ls-files ':(icase)*.jpg' literal

The literal signature tells git to treat all of your characters literally. This would be used if you want to treat characters such as * and ? as themselves, rather than as wildcards. Unless your repository has filenames with * or ?, I don't expect that this signature would be used too often.

git log ':(literal)*.js' # returns log for the file '*.js' glob

When I started learning pathspecs, I noticed that wildcards worked differently than I was used to. Typically I see a single asterisk * as being a wildcard that does not match anything through directories and consecutive asterisks (**) as a “deep” wildcard that does match names through directories. If you would prefer this style of wildcards, you can use the glob magic signature!

This can be useful if you want more fine-grained control over how you search through your project’s directory structure. As an example, take a look at how these two git ls-files can search through a React project.

git ls-files ':(glob)src/components/*/*.jsx' # 'top level' jsx components git ls-files ':(glob)src/components/**/*.jsx' # 'all' jsx components attr

Git has the ability to set “attributes” to specific files. You can set these attributes using a .gitattributes file.

# .gitattributes src/components/vendor/* vendored # sets 'vendored' attribute src/styles/vendor/* vendored

Using the attr magic signature can set attribute requirements for your pathspec. For example, we might want to ignore the above files from a vendor.

git ls-files ':(attr:!vendored)*.js' # searches for non-vendored js files git ls-files ':(attr:vendored)*.js' # searches for vendored js files exclude

Lastly, there is the “exclude'” magic signature (shorthand of :! or :^). This signature works differently from the rest of the magic signatures. After all other pathspecs have been resolved, all pathspecs with an exclude signature are resolved and then removed from the returned paths. For example, you can search through all of your .js files while excluding the .spec.js test files.

git grep 'foo' -- '*.js' ':(exclude)*.spec.js' # search .js files excluding .spec.js git grep 'foo' -- '*.js' ':!*.spec.js' . # shorthand for the same Combining signatures

There is nothing limiting you from using multiple magic signatures in a single pathspec! You can use multiple signatures by separating your magic words with commas within your parenthesis. For example, you can do the following if you’d like to match from the base of your repository (using top), case insensitively (using icase), using only authored code (ignoring vendor files with attr), and using glob-style wildcards (using glob).

git ls-files -- ':(top,icase,glob,attr:!vendored)src/components/*/*.jsx'

The only two magic signatures that you are unable to combine are glob and literal, since they both affect how git deals with wildcards. This is referenced in the git glossary with perhaps my favorite sentence that I have ever read in any documentation.

Glob magic is incompatible with literal magic.

Pathspecs are an integral part of many git commands, but their flexibility is not immediately accessible. By learning how to use wildcards and magic signatures you can multiply your command of the git command line.

The post Git Pathspecs and How to Use Them appeared first on CSS-Tricks.

Categories: Design

Automatically compress images on Pull Requests

Thu, 09/19/2019 - 07:19

Sarah introduced us to GitHub Actions right after it dropped about a year ago. Now they have improved the feature and are touting its CI/CD abilities. Run tests, do deployment, do whatever stuff computers do! It's essentially a YAML file that says run this, then this, then this, etc., with configuration.

GitLab kinda paved the way on this particular feature, although you don't get the machines for free on GitLab, nor does it seems like there is an ecosystem of tasks to build your actions workflow from.

It's that ecosystem of tasks that I would think makes this especially interesting. "Democratizing DevOps," if I'm feeling saucy. Karolina Szczur and Ben Schwarz's new action to automatically optimize all images in a pull request showcases that. This makes it almost trivially easy to add to any Git(Hub)-based workflow and has huge obvious wins. Perhaps the future is peicing together our own pipelines from open-source efforts like this as needed.

Looks nice, eh?

Direct Link to ArticlePermalink

The post Automatically compress images on Pull Requests appeared first on CSS-Tricks.

Categories: Design

Web Developer Search History

Wed, 09/18/2019 - 14:41

Sophie Koonin blogged "Everything I googled in a week as a professional software engineer," which was a fascinating look into the mind of a web developer and what they need to look up during day-to-day work. We all joke that we just Google stuff (or StackOverflow stuff) we don't know off the bat and copy and paste, but this is more to the heart of it.

A few just from one day:

react-apollo release notes
jest silence warnings - don’t judge me, ok?
semantic HTML contact details - wanted to check if the <address> tag was relevant here
dominos accessibility - popcorn.gif

Gift Egwuenu followed up with "The Art of Googling."

A few:

  • Filter an Array with JavaScript - I was working on a chore and needed to see how the filter method works
  • Take a screenshot on Mac
  • Center a div with Grid
  • Undo a git commit
  • Grid with React Native - checking if this was a thing with React Native

Reading these, I alternate from "eeeeesh, good luck" to "I wish I could have been there because I could have probably helped."

This is a fun little blogging trend I'd love to see more people do. (Reminds me of that "How we do CSS at [company]" thing.)

I'll do my last few days, unearthed from Google's My Activity area, filtered to search-only.

This was just a couple of hours yesterday morning, with some noise and distracting stuff removed:

  • Sara Vieira - Was looking for her Twitter to credit a tweet.
  • javascript closest - Was looking for the DOM version rather than the jQuery version
  • jquery parent - Confirming that was a real API it had
  • minimap moz element - Finding an article that was a great use case for the
  • element() function in CSS
  • web unleashed - Going to this conference later in the week and needed to look at the site
  • server log analytics
  • states in australia
  • view html source in spark mail
  • bat crypto
  • html validator
  • html unescape
  • should email html have title?
  • window.scrollto
  • useCallback
  • rails rss feed content type
  • content type for rss feeds
  • river city girls

You next! You next!

The post Web Developer Search History appeared first on CSS-Tricks.

Categories: Design

How Web Content Can Affect Power Usage

Wed, 09/18/2019 - 14:40

Because we know that all people with battery-powered devices are constantly concerned about their battery levels, and that websites are significant consumers of that battery power, we should probably think about this stuff a lot more than we do.

I'd expect the browser itself to be our main ally here, doing smart things to reduce power consumption without us developers needing to think too much about it. But we've learned over the years that it's always a shared responsibility. We regularly need to help the browser do its job the best it can (think responsive images and will-change).

Some direct tips from Benjamin Poulain and Simon Fraser's article:

  • Minimize the use of timers to avoid waking up the CPU. Try to coalesce timer-based work into a few, infrequent timers. Lots of uncoordinated timers which trigger frequent CPU wake-ups are much worse than gathering that work into fewer chunks.
  • Minimize continually animating content, like animated images and auto-playing video. Be particularly vigilant to avoid “loading” spinner GIFs or CSS animations that continually trigger painting, even if you can’t see them. IntersectionObserver can be used to run animations only when they are visible.
  • Use declarative animations (CSS Animations and Transitions) where possible. The browser can optimize these away when the animating content is not visible, and they are more efficient than script-driven animation.
  • Avoid network polling to obtain periodic updates from a server. Use WebSockets or Fetch with a persistent connection, instead of polling.

I'd like to see more developer tooling along the lines of how macOS makes it easy to see apps that are demanding significant power:

WebKit DevTools does has it:

We used to have a Battery Status API, but that's been deprecated, so not a big part of the story right now.

I was just at the Web Unleashed conferencewhere Kyle Simpson talked about this rather directly in his keynote lecture. His main idea is that we should ask users a bit more directly and solicit their preferences. Hey user, are you in a situation where you want to use as little battery power as possible? Tell us and we'll do what we can to make that happen (even on a site-by-site basis).

Direct Link to ArticlePermalink

The post How Web Content Can Affect Power Usage appeared first on CSS-Tricks.

Categories: Design

A Comparison of Static Form Providers

Wed, 09/18/2019 - 07:17

Let’s attempt to coin a term here: "Static Form Provider." You bring your HTML <form>, but don’t worry about the back-end processing that makes it work. There are a lot of these services out there!

Static Form Providers do all tasks like validating, storing, sending notifications, and integrating with other APIs. It’s lovely when you can delegate big responsibilities like this. The cost? Typically a monthly or annual subscription, except for a few providers and limited plans. The cost is usually less than fancier "form builders" that help you build the form itself and process it.

In this article, we are going to review some of the most popular static form providers:

Before moving forward, just a note that the information for these comparisons came from visiting the site for each product and learning about the included features. Once I got all the information, I sent an email to each provider to confirm the list of features. Some of them confirmed, some didn't. Special thanks to Kwes, FormKeep, Formspree, FormSubmit, formX, and Netlify Forms teams for confirming.

Form building components and validation Name Custom Components Front-End Validation Back-End Validation Kwes ✅ ✅ ✅ Basin ❌ ❌ ❌ FieldGoal Unable to confirm Unable to confirm Unable to confirm FormCarry ❌ ❌ ❌ FormKeep ✅ ❌ ❌ Formspree ❌ ❌ ❌ FormSubmit ❌ ❌ ❌ formX ❌ ❌ ❌ Getform ❌ ❌ ❌ Netlify Forms ❌ ❌ ❌

Components for building a form are HTML input elements, like text inputs, textareas, checkboxes, and radio buttons. When using a static form, most providers require adding custom HTML attributes. By providing the custom URL in the form action attribute, the form gets submitted on the provider’s side where it gets stored.

If you are looking for a form builder, FormKeep has a form designer feature. That means you embed custom HTML and JavaScript files in the page, and you get a styled form. Otherwise, you have to style the form by yourself.

If you need custom components, like a date picker or card inputs, Kwes and FormKeep are the only providers with this feature. If you want to validate input fields in the browser, you might use third-party libraries or writing your code which means adding additional JavaScript code to the site. Kwes is the only provider that supports front-end validation based on the rules you set in each input component. To enable this feature, you should include additional JavaScript file, which you might do nevertheless. Other static form providers rely only on HTML5 validation.

Kwes is the only provider with back-end validation, too. The rules you set in the front end are passed to the back end side. In case when front-end validation fails, you are safe, the backend validation would work. Other providers don't have this feature; they rely only on spam protection.

Spam protection Name Spam Protection Kwes Artificial intelligence
Automatic Honeypot
Proprietary technology Basin Akismet
Honeypot FieldGoal Provided, but unable to confirm what powers it FormCarry Akismet
reCAPTCHA FormKeep Provided, but unable to confirm what powers it Formspree reCaptcha
Profanity filter
Automated classification FormSubmit reCaptcha
Honeypot formX Provided, but unable to confirm what powers it Getform Akismet
reCAPTCHA Netlify Forms Akismet

Kwes advertises a 99.6% spam block success rate with no setup required.

Once your form is ready for submissions, you might find it hard to deal with spam. That’s why spam protection is essential, especially if you want to keep your sanity and serenity. All providers provide spam protection in this way or another. Some rely on Google reCAPTCHA or Akismet, some on Honeypot techniques, and some use artificial intelligence to get the job done. It is worth noting that adding an additional step to your form, like adding reCAPTCHA might affect the conversion rates on form submissions.

Email notifications Name Confirmations Notifications Email Routing Logic Kwes ✅ ✅ ✅ Basin ✅ ✅ ✅ FieldGoal Unable to confirm Unable to confirm Unable to confirm FormCarry ✅ ✅ ✅ FormKeep ✅ ✅ ✅ Formspree ✅ ✅ ✅ FormSubmit ✅ ✅ ✅ formX ✅ ✅ ✅ Getform ✅ ✅ ✅ Netlify Forms ✅ ✅ ✅

Email confirmations are essential if you want to provide a fast response to your users. With a contact form, for example, you want to get an email for every new submission. That way, you're able to respond to the submission quickly and efficiently.

All providers, except FieldGoal, have confirmation, notification, and email routing logic features. You could set up an email form element which would be used to send an email automatically to the user with confirmation about the submission.

Some providers have other sorts of notifications besides email, like push notifications or Slack messages, which might be handy.

White labeling Name White Label Kwes ✅ Basin ✅ FieldGoal Unable to confirm FormCarry ✅ FormKeep ✅ Formspree ✅ FormSubmit ✅ formX ✅ Getform ✅ Netlify Forms ✅

When communicating via email notifications with your clients, you might want to use your brand and style. It creates better awareness and that way you familiarize your clients with your product. All providers offer this feature, with the exception of FieldGoal, which I was unable to confirm (although it might be under paid plans).

Custom redirects Name Custom Redirects Kwes ✅ Basin ✅ FieldGoal Unable to confirm FormCarry ✅ FormKeep ✅ Formspree ✅ FormSubmit ✅ formX ✅ Getform ✅ Netlify Forms ✅

Once you have captured a response from your user, you may want to let the user continue using your site. Also, you might want to communicate to the user that the submission was received. This feature is called "custom redirect," and every provider has this feature (with another exception for FieldGoal because I was unable to confirm). Note that this feature might not be available in a free plan and require a paid or upgraded account.

Upload storage Name Upload Storage Kwes 200MB per file
20GB storage Basin 100MB per submission FieldGoal Amazon S3 FormCarry 5MB per file
5GB storage FormKeep Included, but unconfirmed storage amounts Formspree 10MB per file
10GB storage FormSubmit Included, but unconfirmed storage amounts formX ❌ Getform 25MB per submission
25GB storage Netlify Forms 1GB storage

Not every static form provider provides file storage. For example, formX doesn't provide it at all. In most cases, this feature is available under paid plans. You might want to invest additional time to find out which provider offers the best service for you. Be sure to look specifically at single file size and form submission size limitations.

Data export Name Data Export Kwes ✅ Basin ✅ FieldGoal Unable to confirm FormCarry ✅ FormKeep ✅ Formspree ✅ FormSubmit ✅ formX ✅ Getform ✅ Netlify Forms ✅

Data export is important feature if you want to use it for analysis or for import to third-party software. Most providers offers CSV and JSON exports, which are the most commonly used ones.

API access Name API Access Kwes ✅ Basin ✅ FieldGoal Unable to confirm FormCarry ✅ FormKeep ✅ Formspree ✅ FormSubmit ✅ formX On demand Getform ❌ Netlify Forms ✅

If you want to control your data submissions by building custom application or script, you might benefit from having API access. Most providers have this feature, except Getform. formX offers it, but only on demand.

Webhooks/Zapier Name Webhooks Zapier Kwes ✅ ✅ Basin ✅ ✅ FieldGoal Unable to confirm ✅ FormCarry ✅ ✅ FormKeep ✅ ✅ Formspree ✅ ✅ FormSubmit ✅ ❌ formX ✅ ✅ Getform ✅ ✅ Netlify Forms ✅ ✅

When building a custom application or a script is out of budget, you might want to use webhooks to integrate data submissions with third-party software. Zapier is one of the most commonly used services for this, and only FormSubmit doesn't support it (though it does support webhooks).

Analytics Name Analytics Kwes ❌ Basin ✅ FieldGoal Unable to confirm FormCarry ❌ FormKeep ✅ Formspree ❌ FormSubmit ❌ formX ✅ Getform ❌ Netlify Forms ❌

Analytics for static forms is a neat feature that could offer beneficial insight into how your form is performing. It may help you understand how your users interact with it, and you may spot ways to improve the form submission experience as a result. This feature is the least supported of all other features. Only Basin, FormKeep, and formX provide it.

Plan comparison Name Plan 1 Plan 2 Plan 3 Plan 4 Kwes Free Tier
Build spam-protected, and validated forms quicker than ever.

1 Website
Unlimited Forms
50 Spam Blocks Bronze Tier
Unlimited spam blocks, more form tools, and submissions.
1 Website
Unlimited Forms
Unlimited Spam Blocks Silver Tier
Build more powerful forms with integrations and webhooks.
3 Websites
Unlimited Forms
Unlimited Spam Blocks
4 Users Gold Tier
Enjoy more team members and everything with increased limits.
10 Websites
Unlimited Forms
Unlimited Spam Blocks
11 Users Basin Standard Tier
$4.17/mo. (billed annually) Premium Tier
$12.50/mo. (billed annually) FieldGoal Single Tier
1 form
$5/mo. Freelancer Tier
5 forms
$15/mo. Studio Tier
15 forms
$39/mo. Agency Tier
50 forms
$79/mo. FormCarry Baby Tier
Free Basic Tier
$15/mo. Growth Tier
$40/mo. FormKeep Starter Tier
$4.99/mo. Starter Pack
$7.50 per form per month Freelancer Tier
$5.90 per form per month Formspree Free Tier
$0/mo. Gold Tier
$10/mo. Platform Tier
$40/mo. FormSubmit Unlimited formX Free Tier
100 submissions max. Starter Tier
$4.99/mo. SMBs & Freelancers
$49.99/mo. Business & Agencies
$99.99/mo. Getform Free Tier
$0/mo. Basic Tier
Perfect for small businesses
$90 per year Agency Tier
$290 annually Enterprise Tier
$690 annually Netlify Forms Level 0
100 submissions/mo.
10MB uploads/mo. Level 1
$19/mo. per site
1,000 submissions/mo.
1GB uploads/mo. Level 2
Custom pricing and limits

Static form providers have different plans, from entirely free plans and trials, to enterprise plans for every business need. Depending on a plan, you might have different features enabled. For example, FormSubmit is the only provider that offers all of its features for free, though it doesn't support every feature we've covered here. You will want to invest some time to learn about which features that are most important for you and your product or business. Then go ahead and decide on which provider is most suitable for your needs.

Wrapping up

Having a form of any kind is a must-have for a large number of sites. When you use a static site generator, you might discover that static form providers make adding forms to a site almost trivial. By following a few rules for enabling static forms, you could benefit from essential features like spam protection and email notifications.

I have been using Kwes for a while now and I can honestly tell you it is a great product that fulfills all of my needs. It has smart spam protection, an easy-to-use dashboard, and impressive validation, both on the front end and back end.

Before choosing your static form providers, be sure to put down all requirements to the paper, and then find your perfect provider.

The post A Comparison of Static Form Providers appeared first on CSS-Tricks.

Categories: Design

Two Browsers Walked Into a Scrollbar

Wed, 09/18/2019 - 07:16

Surprise: scrollbars are complicated, especially cross-browser and cross-platform.

Sometimes they take up space and sometimes they don't. Sometimes that is affected by a setting and sometimes it isn't. Sometimes you can see them and sometimes you can't unless you're actually scrolling. Styling them is handled in all sorts of different ways, including some very recent developments.

Follow Zach's journey toward thinner, native, user-preference-respecting, more aesthetic scrollbars, particularly for element-level scrollbars that ends up here.

Direct Link to ArticlePermalink

The post Two Browsers Walked Into a Scrollbar appeared first on CSS-Tricks.

Categories: Design

A Color Picker for Product Images

Tue, 09/17/2019 - 13:39

Sounds kind of like a hard problem doesn't it? We often don't have product shots in thousands of colors, such that we can flip out the <img src="product-red.jpg" alt="red product"> with <img src="product-blue.jpg" alt="blue product">. Nor do we typically have products in a vector format such that we can apply SVG fills to them and such.

There is a clever way to do it though, even when your product shots are bitmap graphic files, like JPG or PNG. Kyle Wetton demonstrates, and it's essentially:

  1. Make a vector path that covers the area on the JPG that should change color (probably in Photoshop with the Pen Tool and exporting the vector).
  2. Place that solid vector area exactly on top of the product JPG.
  3. mix-blend-mode: multiply; the SVG.
  4. Change the fill color on the SVG as desired.

Here's the super cool demo I think it originated from:

See the Pen
Color this sofa! – SVG + Blend Mode trick
by Kyle Wetton (@kylewetton)
on CodePen.

And the demo from the article:

See the Pen
Dynamic Colour Picking - Part 3

on CodePen.

The post A Color Picker for Product Images appeared first on CSS-Tricks.

Categories: Design

Overflow And Data Loss In CSS

Tue, 09/17/2019 - 13:38

"Data Loss" is a funny term. My brain thinks of like packet loss on the way from the server to your browser, resulting in missing content in files. Perhaps it is that on some level, but in CSS parlance, it has to do with the overflow property. Too much content for sized container + hidden overflow = data loss.

But it isn't only overflow that can cause this "data loss." Alignment can cause data loss too. Imagine flexbox or grid with some alignment that causes content to be pushed off the top or left edges of the screen, where scrollbars do not venture.

Rachel Andrew:

To prevent accidental data loss caused by alignment, CSS now has some new keywords which can be used along with the alignment properties. These are specified in the Box Alignment specification — the specification which deals with alignment across all layout methods including Grid and Flexbox. They are currently only supported in Firefox. In our example above, if we set align-items: safe center, then the final item would become aligned to start rather than forcing the content to be centered. This would prevent the data loss caused by the item being centered and therefore pushed off the side of the viewport.

Direct Link to ArticlePermalink

The post Overflow And Data Loss In CSS appeared first on CSS-Tricks.

Categories: Design

A Proof of Concept for Making Sass Faster

Tue, 09/17/2019 - 07:19

At the start of a new project, Sass compilation happens in the blink of an eye. This feels great, especially when it’s paired with Browsersync, which reloads the stylesheet for us in the browser. But, as the amount of Sass grows, compilation time increases. This is far from ideal.

It can be a real pain when the compilation time creeps over one or two seconds. For me, that's enough time to lose focus at the end of a long day. So, I would like to share a solution that's available in a WordPress CSS Editor called Microthemer, as a proof of concept.

This is a two-part article. The first part is for the attention of Sass users. We will introduce the basic principles, performance results, and an interactive demo. The second part covers the nuts and bolts of how Microthemer makes Sass faster. And considers how to implement this as an npm package that can deliver fast, scalable Sass to a much broader community of developers.

How Microthemer compiles Sass in an instant

In some ways, this performance optimisation is simple. Microthemer just compiles less Sass code. It doesn’t intervene with Sass’ internal compilation process.

In order to feed the Sass compiler less code, Microthemer maintains an understanding of Sass entities used throughout the code base, like variables and mixins. When a selector is edited, Microthemer only compiles that single selector, plus any related selectors. Selectors are related if they make use of the same variables for instance, or one selector extends another. With this system, Sass compilation remains as fast with 3000 selectors as it does with a handful.

Performance results

With 3000 selectors, the compile time is around 0.05 seconds. It varies, of course. Sometimes it might be closer to 0.1 seconds. Other times the compilation happens as fast as 0.01 seconds (10ms).

To see for yourself, you can watch a video demonstration. Or mess around with the online Microthemer playground (see instructions below).

Online Microthemer playground

The online playground makes it easy to experiment with Microthemer yourself.

  1. Go to the online Microthemer playground.
  2. Enable support for Sass via General → Preferences → CSS / SCSS → Enable SCSS.
  3. Go to View → full code editor → on to add global variables, mixins, and functions.
  4. Switch back to the main UI view (View → full code editor → off).
  5. Create selectors via the Target button.
  6. Add Sass code via the editor to the left of the Font property group.
  7. After each change, you can see what code Microthemer included in the compile process via View → Generated CSS → Previous SCSS compile.
  8. To see how this works at scale, you can import vanilla CSS from a large stylesheet into Microthemer via Packs → Import → CSS stylesheet (importing Sass isn't supported - yet).
Do you want this as an npm package?

Microthemer’s selective compilation technique could also be delivered as an npm package. But the question is, do you see a need for this? Could your local Sass environment do with a speed boost? If so, please leave a comment below.

The rest of this article is aimed at those who develop tools for the community. As well as those who might be curious about how this challenge was tackled.

The Microthemer way to compile Sass

We will move on to some code examples shortly. But first, let's consider the main application goals.

1. Compile minimal code

We want to compile the one selector being edited if it has no relationship with other selectors, or multiple selectors with related Sass entities — but no more than necessary.

2. Responsive to code changes

We want to remove any perception of waiting for Sass to compile. We also don't want to crunch too much data between user keystrokes.

3. Equal CSS output

We want to return the same CSS a full compile would generate, but for a subset of code.

Sass examples

The following code will serve as a point of reference throughout this article. It covers all of the scenarios our selective compiler needs to handle. Such as global variables, mixin side-effects, and extended selectors.

Variables, functions, and mixins $primary-color: green; $secondary-color: red; $dark-color: black; @function toRem($px, $rootSize: 16){ @return #{$px / $rootSize}rem; } @mixin rounded(){ border-radius: 999px; $secondary-color: blue !global; } Selectors .entry-title { color: $dark-color; } .btn { display: inline-block; padding: 1em; color: white; text-decoration: none; } .btn-success { @extend .btn; background-color: $primary-color; @include rounded; } .btn-error { @extend .btn; background-color: $secondary-color; } // Larger screens @media (min-width: 960px) { .btn-success { border:4px solid darken($primary-color, 10%); &::before { content: "\2713"; // unicode tick margin-right: .5em; } } } The Microthemer interface

Microthemer has two main editing views.

View 1: Full code

We edit the full code editor in the same way as a regular Sass file. That’s where global variables, functions, mixins, and imports go.

View 2: Visual

The visual view has a single selector architecture. Each CSS selector is a separate UI selector. These UI selectors are organized into folders.

Because Microthemer segments individual selectors, analysis happens at a very granular scale — one selector at a time.

Here’s a quick quiz question for you. The $secondary-color variable is set to red at the top of the full code view. So why is the error button in the previous screenshots blue? Hint: it has to do with mixin side effects. More on that shortly.

Third party libraries

A huge thanks to the authors of the following JavaScript libraries Microthemer uses:

  • Gonzales PE - This converts Sass code to an abstract syntax tree (AST) JavaScript object.
  • Sass.js - This converts Sass to CSS code in the browser. It uses web workers to run the compilation on a separate thread.
Data objects

Now for the nitty gritty details. Figuring out an appropriate data structure took some trial and error. But once sorted, the application logic fell into place naturally. So we’ll start by explaining the main data stores, and then finish with a brief summary of the processing steps.

Microthemer uses four main JavaScript objects for storing application data.

  1. projectCode: Stores all project code partitioned into discreet items for individual selectors.
  2. projectEntities: Stores all variables, functions, mixins, extends and imports used in the project, as well as the locations of where these entities are used.
  3. connectedEntities: Stores the connections one piece of code has with project Sass entities.
  4. compileResources: Stores the selective compile data following a change to the code base.

The projectCode object allows us to quickly retrieve pieces of Sass code. We then combine these pieces into a single string for compilation.

  • files: With Microthemer, this stores the code added to the full code view described earlier. With an npm implementation, fileswould relate to actual .sass or .scss system files.
  • folders: Microthemer’s UI folders that contain segmented UI selectors.
  • index: The order of a folder, or a selector within a folder.
  • itemData: The actual code for the item, explained further in the next code snippet.
var projectCode = { // Microthemer full code editor files: { full_code: { index: 0, itemData: itemData } }, // Microthemer UI folders and selectors folders: { content_header: { index:100, selectors: { '.entry-title': { index:0, itemData: itemData }, } }, buttons: { index:200, selectors: { '.btn': { index:0, itemData: itemData }, '.btn-success': { index:1, itemData: itemData }, '.btn-error': { index:2, itemData: itemData } } } } }; itemData for .btn-success selector

The following code example shows the itemData for the .btn-success selector.

  • sassCode: Used to build the compilation string.
  • compiledCSS: Stores compiled CSS for writing to a stylesheet or style node in the document head.
  • sassEntities: Sass entities for single selector or file. Allows for before and after change analysis, and is used to build the projectEntities object.
  • mediaQueries: Same data as above, but for a selector used inside a media query.
var itemData = { sassCode: ".btn-success { @extend .btn; background-color: $primary-color; @include rounded; }", compiledCSS: ".btn-success { background-color: green; border-radius: 999px; }", sassEntities: { extend: { '.btn': { values: ['.btn'] } }, variable: { primary_color: { values: [1] } }, mixin: { rounded: { values: [1] } } }, mediaQueries: { 'min-width(960px)': { sassCode: ".btn-success { border:4px solid darken($primary-color, 10%); &::before { content: '\\2713'; margin-right: .5em; } }", compiledCSS: ".btn-success::before { content: '\\2713'; margin-right: .5em; }", sassEntities: { variable: { primary_color: { values: [1] } }, function: { darken: { values: [1] } } } } } }; projectEntities

The projectEntities object allows us to check which selectors use particular Sass entities.

  • variable, function, mixin, extend: The type of Sass entity.
  • E.g. primary_color: The Sass entity name. Microthemer normalizes hyphenated names because Sass uses hyphens and underscores interchangeably.
  • values: An array of declaration values or instances. Instances are represented by the number 1. The Gonzales PE Sass parser converts numeric declaration values to strings. So I’ve elected to use the integer 1 to flag instances.
  • itemDeps: An array of selectors that makes use of the Sass entity. This is explained further in the next code snippet.
  • relatedEntities: Our rounded mixin has a side effect of updating the global $secondary-color variable to blue, hence the blue error button. This side effect makes the rounded and $secondary-color entities co-dependent. So, when the $secondary-color variable is included, the roundedmixin should be included too, and vice versa.
var projectEntities = { variable: { primary_color: { values: ['green', 1], itemDeps: itemDeps }, secondary_color: { values: ["red", "blue !global", 1], itemDeps: itemDeps, relatedEntities: { mixin: { rounded: {} } } }, dark_color: { values: ["black", 1], itemDeps: itemDeps } }, function: { darken: { values: [1] }, toRem: { values: ["@function toRem($px, $rootSize: 16){↵ @return #{$px / $rootSize}rem;↵}", 1], itemDeps: itemDeps } }, mixin: { rounded: { values: ["@mixin rounded(){↵ border-radius:999px;↵ $secondary-color: blue !global;↵}", 1], itemDeps: itemDeps, relatedEntities: { variable: { secondary_color: { values: ["blue !global"], } } } } }, extend: { '.btn': { values: ['.btn', '.btn'], itemDeps: itemDeps } } }; itemDeps for the $primary-color Sass entity

The following code example shows the itemDeps for the $primary-color (primary_color) variable. The $primary-color variable is used by two forms of the .btn-success selector, including a selector inside the min-width(960px) media query.

  • path: Used to retrieve selector data from the projectCode object.
  • mediaQuery: Used when updating style nodes or writing to a CSS stylesheet.
var itemDeps = [ { path: ["folders", 'header', 'selectors', '.btn-success'], }, { path: ["folders", 'header', 'selectors', '.btn-success', 'mediaQueries', 'min-width(960px)'], mediaQuery: 'min-width(960px)' } ]; connectedEntities

The connectedEntities object allows us to find related pieces of code. We populate it following a change to the code base. So, if we were to remove the font-size declaration from the .btn selector, the code would change from this:

.btn { display: inline-block; padding: 1em; color: white; text-decoration: none; font-size: toRem(21); } this:

.btn { display: inline-block; padding: 1em; color: white; text-decoration: none; }

And we would store Microthemer’s analysis in the following connectedEntities object.

  • changed: The change analysis, which captures the removal of the toRem function.

    • actions: an array of user actions.
    • form: Declaration (e.g. $var: 18px) or instance (e.g. font-size: $var).
    • value: A text value for a declaration, or the integer 1 for an instance.
  • coDependent: Extended selectors must always compile with the extending selector, and vice versa. The relationship is co-dependent. Variables, functions, and mixins are only semi-dependent. Instances must compile with declarations, but declarations do not need to compile with instances. However, Microthemer treats them as co-dependent for the sake of simplicity. In the future, logic will be added to filter out unnecessary instances, but this has been left out for the first release.
  • related: the rounded mixin is related to the $secondary-color variable. It updates that variable using the global flag. The two entities are co-dependent; they should always compile together. But in our example, the .btn selector makes no use of the rounded mixin. So, the related property below is not populated with anything.
var connectedEntities = { changed: { function: { toRem: { actions: [{ action: "removed", form: "instance", value: 1 }] } } }, coDependent: { extend: { '.btn': {} } }, related: {} }; compileResources

The compileResources object allows us to compile a subset of code in the correct order. In the previous section we removed the font-size declaration. The code below shows how the compileResources object would look after that change.

  • compileParts: An array of resources to be compiled.

    • path: Used to update the compiledCSS property of the relevant projectCodeitem.
    • sassCode: Used to build the sassString for compilation. We append a CSS comment to each piece (/*MTPART*/) . This comment is used to split the combined CSS output into the cssParts array.
  • sassString: A string of Sass code that compiles to CSS.
  • cssParts: CSS output in the form of an array. The array keys for cssParts line up with the compileParts array.
var compileResources = { compileParts: [ { path: ["files", "full_code"], sassCode: "/*MTFILE*/$primary-color: green; $secondary-color: red; $dark-color: black; @function toRem($px, $rootSize: 16){ @return #{$px / $rootSize}rem; } @mixin rounded(){ border-radius:999px; $secondary-color: blue !global;}/*MTPART*/" }, { path: ["folders", "buttons", ".btn"], sassCode: ".btn { display: inline-block; padding: 1em; color: white; text-decoration: none; }/*MTPART*/" }, { path: ["folders", "buttons", ".btn-success"], sassCode: ".btn-success { @extend .btn; background-color: $primary-color; @include rounded; }/*MTPART*/" }, { path: ["folders", "buttons", ".btn-error"], sassCode: ".btn-error { @extend .btn; background-color: $secondary-color; }/*MTPART*/" } ], sassString: "/*MTFILE*/$primary-color: green; $secondary-color: red; $dark-color: black; @function toRem($px, $rootSize: 16){ @return #{$px / $rootSize}rem; } @mixin rounded(){ border-radius:999px; $secondary-color: blue !global;}/*MTPART*/"+ ".btn { display: inline-block; padding: 1em; color: white; text-decoration: none;}/*MTPART*/"+ ".btn-success {@extend .btn; background-color: $primary-color; @include rounded;}/*MTPART*/"+ ".btn-error {@extend .btn; background-color: $secondary-color;}/*MTPART*/", cssParts: [ "/*MTFILE*//*MTPART*/", ".btn, .btn-success, .btn-error { display: inline-block; padding: 1em; color: white; text-decoration: none;}/*MTPART*/", ".btn-success { background-color: green; border-radius: 999px;}/*MTPART*/", ".btn-error { background-color: blue;}/*MTPART*/" ] }; Why were four resources included?
  1. full_code: The toRem Sass entity changed and the full_code resource contains the toRem function declaration.
  2. .btn: the selector was edited.
  3. .btn-success: Uses @extend .btn and so it must always compile with .btn. The combined selector becomes .btn, .btn-success.
  4. .btn-error: This also uses @extend .btn and so must be included for the same reasons as .btn-success.

Two selectors are not included because they are not related to the .btn selector.

  1. .entry-title
  2. .btn-success (inside the media query)
Recursive resource gathering

Aside from the data structure, the most time consuming challenge was figuring out how to pull in the right subset of Sass code. When one piece of code connects to another piece, we need to check for connections on the second piece too. There is a chain reaction. To support this, the following gatherCompileResources function is recursive.

  • We loop the connectedEntities object down to the level of Sass entity names.
  • We use recursion if a function or mixin has side effects (like updating global variables).
  • The checkObject function returns the value of an object at a particular depth, or false if no value exists.
  • The updateObject function sets the value of an object at a particular depth.
  • We add dependent resources to the compileParts array, using absoluteIndex as the key.
  • Microthemer calculates absoluteIndex by adding the folder index to the selector index. This works because folder indexes increment in hundreds, and the maximum number of selectors per folder is 40, which is fewer than one hundred.
  • We use recursion if resources added to the compileParts array also have co-dependent relationships.
function gatherCompileResources(compileResources, connectedEntities, projectEntities, projectCode, config){ let compileParts = compileResources.compileParts; // reasons: changed / coDependent / related const reasons = Object.keys(connectedEntities); for (const reason of reasons) { // types: variable / function / mixin / extend const types = Object.keys(connectedEntities[reason]); for (const type of types) { // names: e.g. toRem / .btn / primary_color const names = Object.keys(connectedEntities\[reason\][type]); for (const name of names) { // check side-effects for Sass entity (if not checked already) if (!checkObject(config.relatedChecked, [type, name])){ updateObject(config.relatedChecked, [type, name], 1); const relatedEntities = checkObject(projectEntities, [type, name, 'relatedEntities']); if (relatedEntities){ compileParts = gatherCompileResources( compileResources, { related: relatedEntities }, projectEntities, projectCode, config ); } } // check if there are dependent pieces of code const itemDeps = checkObject(projectEntities, [type, name, 'itemDeps']); if (itemDeps && itemDeps.length > 0){ for (const dep of itemDeps) { let path = dep.path, resourceID = path.join('.'); if (!config.resourceAdded[resourceID]){ // if we have a valid resource let resource = checkObject(projectCode, path); if (resource){ config.resourceAdded[resourceID] = 1; // get folder index + resource index let absoluteIndex = getAbsoluteIndex(path); // add compile part compileParts[absoluteIndex] = { sassCode: resource.sassCode, mediaQuery: resource.mediaQuery, path: path }; // if resource is co-dependent, pull in others let coDependent = getCoDependent(resource); if (coDependent){ compileParts = gatherCompileResources( compileResources, { coDependent: coDependent }, projectEntities, projectCode, config ); } } } } } } } } return compileParts; } The application process

We’ve covered the technical aspects now. To see how it all ties together, let’s walk through the data processing steps.

From keystroke to style render
  1. A user keystroke fires the textarea change event.
  2. We convert the single selector being edited into a sassEntities object. This allows for comparison with the pre-edit Sass entities: projectCode > dataItem > sassEntities.
  3. If any Sass entities changed:

    • We update projectCode > dataItem > sassEntities.
    • If an @extend rule changed:
      • We search the projectCode object to find matching selectors.
      • We store the path for matching selectors on the current data item: projectCode > dataItem > sassEntities > extend > target > [ path ].
    • We rebuild the projectEntities object by looping over the projectCode object.
    • We populate connectedEntities > changed with the change analysis.
    • If extend, variable, function, or mixin entities are present:

      • We populate connectedEntities > coDependent with the relevant entities.
  4. The recursive gatherCompileResources function uses the connectedEntities object to populate the compileResources object.
  5. We concatenate the compileResources > compileParts array into a single Sass string.
  6. We compile the single Sass string to CSS.
  7. We use comment dividers to split the output into an array: compileResources > cssParts. This array lines up with the compileResources > compileParts array via matching array keys.
  8. We use resource paths to update the projectCode object with compiled CSS.
  9. We write the CSS for a given folder or file to a style node in the document head for immediate style rendering. On the server-side, we write all CSS to a single stylesheet.
Considerations for npm

There are some extra considerations for an npm package. With a typical NodeJS development environment:

  • Users edit selectors as part of a larger file, rather than individually.
  • Sass imports are likely to play a larger role.
Segmentation of code

Microthemer’s visual view segments individual selectors. This makes parsing code to a sassEntities object super fast. Parsing whole files might be a different story, especially large ones.

Perhaps techniques exist for virtually segmenting system files? But suppose there is no way around parsing whole files. Or it just seems sensible for a first release. Maybe it’s sufficient to advise end users to keep Sass files small for best results.

Sass imports

At the time of writing, Microthemer doesn’t analyse import files. Instead, it includes all Sass imports whenever a selector makes use of any Sass entities. This is an interim first release solution that is okay in the context of Microthemer. But I believe an npm implementation should track Sass usage across all project files.

Our projectCode object already has a files property for storing file data. I propose calculating file indexes relative to the main Sass file. For instance, the file used in the first @import rule would have index: 0, the next, index: 1, and so on. We would need to scan @import rules within imported files to calculate these indexes correctly.

We would need to calculate absoluteIndex differently, too. Unlike Microthemer folders, system files can have any number of selectors. The compileParts array might need to be an object, storing an array of parts for each file. That way, we only keep track of selector indexes within a given file and then concatenate the compileParts arrays in the order of the files.


This article introduces a new way to compile Sass code, selectively. Near instant Sass compilation was necessary for Microthemer because it is a live CSS editor. And the word 'live' carries certain expectations of speed. But it may also be desirable for other environments, like Node.js. This is for Node.js and Sass users to decide, and hopefully share their thoughts in the comments below. If the demand is there, I hope an npm developer can take advantage of the points I’ve shared.

Please feel free to post any questions about this in my forum. I'm always happy to help.

The post A Proof of Concept for Making Sass Faster appeared first on CSS-Tricks.

Categories: Design

An Updated List of Our Favorite Jetpack Features for WordPress

Tue, 09/17/2019 - 07:18

It's hard to articulate every reason to use Jetpack for your WordPress site. It's taken us a series of posts to unpack it because it's capable of doing so gosh darn much — a lof of which we put to use right here on CSS-Tricks.

The thing is that Jetpack is very much an active project and keeps improving with each and every release. While we've covered many Jetpack features and uses over the years, it's worth revisiting here in 2019 to compile a list of personal favorites, both classic and new.

It monitors downtime

Wouldn't it be nice to be notified if your site crashes for some reason? Well, Jetpack can monitor your site for you and alert you if something's gone awry. Sure, there are other services, like New Relic or Pingdom that do this as well, but how great is it to not have to integrate yet another service on another platform? That's the benefit of using Jetpack. You're gonna get so many other features with it already, so there's need to look outside for a solution it already provides.

It lets people follow your blog

Let's say you're interested in starting a newsletter so you can send new posts to subscribers and you're looking into services like Mailchimp and Campaign Monitor to carry the lift. At the same time, you're trying to keep costs down and would rather not pay an additional service for this ability.

Well, you guessed it, Jetpack will do that for you. All it takes is flipping on the feature and a subscribe option will be added to your post comment form. Anyone who signs up will receive a notification in their inbox each time a new post is published.

Even better, you can see all your subscribers and flip an additional toggle that allows visitors to subscribe just to follow the comments for a specific post.

It comes with a CDN

Jetpack includes a free Content Delivery Network (CDN) that optimizes and delivers all your static files for you. And that's any static file, including images, videos, CSS and JavaScript. Yep, again, there's really no need to look outside the box to serve the most efficient assets to your visitors and get a hefty speed boost for your site.

Speaking from a cost/value perspective, especially since this feature is available on the free Jetpack plan as well, this tops the charts.

It embeds rich media for you

Want to embed a YouTube video in your post? Or a tweet? Maybe an Instagram post or a gist? A Spotify playlist? Jetpack will embed any and all of those among many, many others with either a simple URL or a WordPress shortcode. And it works whether you're using the Block Editor, the Classic Editor, or even a sidebar widget. It's the easiest way to embed media in a WordPress post with having to get into iframes or touching any code.

Like the other features we've covered here, all it takes to enable this is the flip of a switch, on the Jetpack → Settings → Writing screen.

It adds tons of options to the Block Editor

The new Block Editor (aka Gutenberg) already includes a number of blocks that make editing and publishing posts more flexible and versatile than ever, but Jetpack gives it an extra dose of additional blocks to make designing page/post layouts even more awesome.

In fact, we covered this in greater detail including each of the blocks Jetpack includes and how they can be used.

It supports Markdown

Working in the Block and Visual Editors in WordPress is great most of the time, but have you ever needed to adjust the markup of a post to get things juuuust right? WordPress has the "Text" editor as well as an HTML block, but of course, that only supports HTML.

Markdown is much easier to write and Jetpack lets you use it in the editor.

As you can tell, we love Jetpack around here and think anyone with a WordPress site ought to give it a try. It packs so many features into a single package that you get the same benefit as using dozens of other plugins, without the constant update madness.

The post An Updated List of Our Favorite Jetpack Features for WordPress appeared first on CSS-Tricks.

Categories: Design

CSS-Tricks Chronicle XXXVI

Mon, 09/16/2019 - 12:48

This is one of these little roundups of things going on with myself, this site, and the other sites that are part of the CSS-Tricks family.

I was recently in ZĂĽrich for Front Conference. It was my first time there and I very much enjoyed the city and the lovely staff of the conference. I was terribly jetlagged for my opener talk so I feel like I didn't quite nail it how I wanted to, but whattyagonnado.

It's named "How to Think Like a Front-End Developer" but it's really more like an adaptation of "ooooops I guess we’re full-stack developers now."

I've packed in several more conferences this fall:

  1. 13 SEP 2019 - Web Unleashed - Toronto, Canada
  2. 18 SEP 2019 - Dot All - Montreal, Canada
  3. 30 SEP 2019 - ARTIFACT - Austin, Texas - Use coupon code LASTCHANCE200 for this one.
  4. 13 OCT 2019 - All Things Open - Raleigh, North Carolina
  5. 16 OCT 2019 - JAMstack_conf - San Francisco, California

Speaking of conferences, if you know of any coming up that aren't on our master list of front-end related web conferences, please do a pull request or contact me.

If we've got anything at ShopTalk Show, it's consistency! I don't even remember the last time we've missed a week, and I enjoy making the show just as much now as I ever have.

Perhaps my favorite show of late was just chatting with Dave about what technology we would pick if on a greenfield (from scratch) project under different circumstances.

But mostly we chat with folks like Tyler McGinnis, Adam Argyle, Rachel Andrew, and Lara Hogan.

We're moving right along at CodePen as well!

  • You can now export Pens with a build process, meaning after an npm install, you have an offline version of CodePen to work with. Need to spin up a little processing environment for like Markdown/Sass/React/Babel? Just set up a blank Pen that way, export it, and you've got it.
  • We're building more and more of CodePen in React, and I think we're past the tipping point where the value in that becomes more and more clear. It's a good technological fit for our type of site. For example, we re-wrote how items are displayed and grids-of-items across the site. So now we build some little feature into it like "pinning" items, and it instantly sprinkles out to all the grids on the entire site. Same with filtering and view options.
  • Along those same lines, little moments like this feel very satisfying to me. That's related to our "Private by Default" feature.
  • We released a feature so you can block other users if it comes down to that (as well as report them to us).
  • We released some high contrast syntax highlighting themes that are both more accessible and darn nice to look at.

I got to be on Giant Robots!

On Giant Robots @chriscoyier, cofounder of @CodePen, creator of CSS-Tricks, & host of @ShopTalkShow, candidly shares how a weekend project turned into a full-time business and aspirations for the future.

— thoughtbot (@thoughtbot) September 16, 2019

We started an Instagram account at @real_css_tricks. The plan is just little educational tidbits.

The post CSS-Tricks Chronicle XXXVI appeared first on CSS-Tricks.

Categories: Design

(Why) Some HTML is “optional”

Mon, 09/16/2019 - 09:34

Remy Sharp digs into the history of the web and describes why the <p> tag doesn’t need to be closed like this:

<p>Paragraphs don’t need to be closed <p>Pretty weird, huh?

Remy writes:

Pre-DOM, pre-browsers, the world's first browser was being written by Sir Tim Berners-Lee. There was no reference implementation and certainly no such thing as a parsing specification. The first browser, the, parsed HTML character by character applying styles as it went along. As opposed to today's methods whereby a document object model is built up, then rendered.

[...] The paragraph tag (yes, in upper case) was intended to separate paragraphs, not wrap them.

<P> Paragraph one. <P> Paragraph two. <P> Paragraph three.

Weird, huh! Remy wrote this in response to Chris’ post the other day about optional HTML and how browsers will close certain tags for us or even insert the right elements in place.

Direct Link to ArticlePermalink

The post (Why) Some HTML is “optional” appeared first on CSS-Tricks.

Categories: Design

Web Development Merit Badges

Mon, 09/16/2019 - 07:30
Changed a DNS record and everything worked just fine Comprehended someone else's RegEx Built an accordion from scratch Exited VIM Accidentally created own CMS Pulled off a design you didn’t think you could Told a client/boss "No, we're not doing that." Wrote an HTAccess redirect that included a capture group Refactored a large portion of CSS and didn't break anything Centered an element vertically and horizontally Migrated a database without character encoding issues Pushed to production on Friday and didn't roll it back over the weekend Merged master into a six month old branch Had a neglected site get hacked and spammed Used CSS Grid in production Someone you don't know starred one of your GitHub Repositories Hand-coded a HTML email Gave someone useful feedback on a Pull Request Debugged something for over one hour where the fix was literally one character Solved a bug by taking a nap Became extremely confused by a CORS error Quoted the exact number of hours it took to do the job Renewed an SSL certificate without any drama Found an answer to an issue on StackOverflow Rocked the Checkbox Hack on a project Your personal website hasn't been updated in at least 5 years

The post Web Development Merit Badges appeared first on CSS-Tricks.

Categories: Design

5G Will Definitely Make the Web Slower, Maybe

Mon, 09/16/2019 - 07:27

Scott Jehl has written this wonderful piece about how 5G is on the horizon and how it could cause problems for users. But first, he starts by talking about the overwhelming positive news about it:

[...] as it matures 5G is predicted to improve network speeds dramatically. Carriers are predicting download speeds in 2019 for anywhere from 100Mb to 1 Gbit per second on average.

This is...bonkers! Numbers like this make it seem as though the web’s performance problems are merely a matter of infrastructure. But Scott continues:

Faster networks should fix our performance problems, but so far, they have had an interesting if unintentional impact on the web. This is because historically, faster network speed has enabled developers to deliver more code to users—in particular, more JavaScript code.

During the years 2011 through 2019, 4g coverage spread from 5% to 79% of the world. During that same time period, the median average JavaScript transfer size to mobile devices increased by 611%, from 52kb to 372.9 KB.

When I read this, I thought of how adding extra lanes to highways actually causes more traffic, which is equally weird and counterintuitive. But networks are like highways in this way, as Scott shows above. He continues to look at how most phones won’t be using the latest and greatest tech and we’ll always have to consider the users that are using the slowest devices on the slowest networks, regardless of how fast our connection might be:

As networks improve, we have a huge opportunity to improve the web we build, but it’s on us to take that opportunity, or squander it.

Direct Link to ArticlePermalink

The post 5G Will Definitely Make the Web Slower, Maybe appeared first on CSS-Tricks.

Categories: Design

Sat, 09/14/2019 - 19:03

As long as I can remember the main source for feature support in HTML email clients is Campaign Monitor's guide. Now there is a new player on the block:

HTML email is often joked about in how you have to code for it in such an antiquated way (<table>s! really!) but that's perhaps not a fair shake. 2 years ago Kevin Mandeville talked about how he used CSS grid (not kidding) in an email:

Our Apple Mail audience at Litmus is approximately 30%, so a good portion of our subscriber base is able to see the grid desktop layout.

Where CSS Grid isn't supported (and for device/window widths of less than 850 pixels), we fell back to a one-column layout.

Just like websites, right? They don't have to look the same everywhere, as long as the experience is acceptable everywhere.

RĂ©mi announces the new site:

... we have more than 50 HTML and CSS features tested across 25 emails clients. And we’ve got a lot more coming up in the following weeks and months.

We’re also delighted to present the Email Client Support Scoreboard. For the first time in history, we provide an objective ranking of email clients based on their support for HTML and CSS features.

Interested in grid support? They got it. The data is tucked into Front Matter in Markdown files in the repo.

Direct Link to ArticlePermalink

The post appeared first on CSS-Tricks.

Categories: Design

Where should “Subscribe to Podcast” link to?

Fri, 09/13/2019 - 15:20

For a while, iTunes was the big dog in podcasting, so if you linked "Subscribe to Podcast" to like:

...that would make sense. It's a web URL anyway, so it will work for anyone and has information about the podcast, as well as a list of recent shows you can even listen to right there. For Apple folks, you might be redirected in-app (mobile) or it becomes one click away (desktop). But for folks on Android or Linux or Windows or something, that's not particularly useful.

What are the other possibilities?

Podcasts are essentially dressed up RSS, so giving people a link to the feed isn't out of the question. We do that on both ShopTalk and CodePen Radio:

I like PocketCasts for my podcasts. I feel like this used to be more obvious, but pasting in an RSS link to search does seem to find the feeds.

I would think (and hope!) that most podcast apps have some way to subscribe manually via feed. But... pretty nerdy and probably a little too dangerous for just a "Subscribe to Podcast" link.

For Android specifically, there is a site where you can put your feed URL after "" and get a special page just for that:

They say:

If the listener has a one click supported app on their android device, the App will load automatically.

And clearly there are some options:

I find the most common option on podcasts is to link to a soup of popular options:

I think that's probably a safe thing to do. For one, it signals that you're on top of your game a bit and that your show is working on major platforms. But more importantly, podcast listeners probably know what platform they mainly use and clicking on a link specifically for that platform is probably quite natural.

Speaking of major platforms, Spotify is going big on podcasts, so linking directly to Spotify probably isn't the worst choice you could make.

But there are situations where you only get one link. Instagram is notable for this. No links on posts — only the one link on your profile. You could send them to your website, but of course, with podcasts, the name of the game is making it easy to subscribe. That means getting people right there is best. But also with stuff like tweets, you can't always deliver a smorgasbord of links. Hence the title of this blog post. If you gotta link to just one place to subscribe, where should it be?

Looks like that's what Plink does.

Here's ShopTalk:

Visiting on desktop gets you the smorgasbord of links. Visiting on my iPhone, I get a direct link to Apple Podcasts.

That's what they do:

Auto-open installed Podcast Apps native to listener's iOS, Android, and other mobile and smart watch devices. Each smart link also has a Show Page that desktop users will see with links to that show in Apps like Apple & Google Podcasts, Spotify, Stitcher, Overcast, and other podcatchers.

They apparently use all kinds of data to figure it out.

... will detect the listener's device, geo, and other factors and send them to your show in pre-installed podcast apps.

Anybody can make a redirect link to particular platforms. Like, we could have built and and redirected to those places, but what you get here is fancy auto-detection in a single link.

I signed up for it for ShopTalk, so we'll see if we end up using it much or not.

The post Where should “Subscribe to Podcast” link to? appeared first on CSS-Tricks.

Categories: Design
©2019 Richard Esmonde. All rights reserved.