Archive

Archive for October, 2021

Cash (Tiny jQuery Alternative)

October 11th, 2021 No comments

The README for Cash is straightforward:

Cash is an absurdly small jQuery alternative for modern browsers (IE11+) that provides jQuery-style syntax for manipulating the DOM. Utilizing modern browser features to minimize the codebase, developers can use the familiar chainable methods at a fraction of the file size. 100% feature parity with jQuery isn’t a goal, but Cash comes helpfully close, covering most day to day use cases.

6 KB minified and gzipped, which is even smaller than Zepto. Zepto’s whole point was a smaller jQuery, but it hasn’t been touched in a bunch of years, so there is that too.

I wonder how much smaller Cash would be if it dropped IE 11 support.

jQuery is still huuuuugggggeeee, only just recently having peaked in usage and showing signs of decline. That must be because it comes on most WordPress sites, right?! That’s like 42% of all sites right there.

Anyway, if you tend to reach for jQuery just for some convincing, Cash looks like a nice alternative. I don’t blame you either. Typing $ instead of document.querySelectorAll still feels good to me, not to mention all the other fanciness tucked behind that dollar sign function.

CodePen Embed Fallback

Also worth mentioning: if you’re looking to straight-up remove jQuery from a project, replace-jquery might be worth a look:

Automatically find jQuery methods from existing projects and generate vanilla js alternatives.


The post Cash (Tiny jQuery Alternative) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Categories: Designing, Others Tags:

Exciting New Tools For Designers, October 2021

October 11th, 2021 No comments

There are some spook-tacular finds in this month’s October collection of resources and tools for designers and developers. From interesting tools that can help in the design process to boo-tiful typefaces, there’s something for everyone here.

Here’s what is new for designers this month…

Atropos

Atropos is a lightweight, open-source JavaScript library to create touch-friendly, three-dimensional hover effects. The results are stunning and have a nice parallax style. Everything is highly configurable and customizable. It’s available for JavaScript, React, and Vue.js and has zero dependencies.

CSS Gradient Editor

CSS Gradient Editor helps you create the perfect gradient style – you can start from presets – that you can use in projects. Design a background, fill, or almost any other gradient element you might need, make adjustments or customizations, and then get the CSS with one click so you can use it right away.

Octopus.do

Octopus.do is a fast visual sitemap builder that lets you work in real-time using the content brick method. Share and collaborate in real-time and there’s no signup required to use it.

Pirsch Analytics

Pirsch Analytics is a privacy-friendly, open-source alternative to Google Analytics — lightweight, cookie-free, and easily integrated into any website or directly into your backend. It includes filters to see metrics in the way you want and light and dark modes.

Basic Pattern Repository

Basic Pattern Repository is a collection of simple SVG patterns for projects. Everything is rooted in a simple style to help push projects along quicker. You can get it via GitHub or as a Figma Library.

Blobr

Blobr is a way to get a branded API portal, manage access, and monitor usage all in one place. Customize everything to fit your brand and the tool grows as you do with the ability to increase or change capacity. Plus, it is easy to set up and free to use.

HEXplorer

HEXplorer helps you better understand something you use all the time – HEX colors. This pen by Rob DiMarzo shows how the values for different colors come together to provide greater comprehension when it comes to this color format.

CCCreate

CCCreate is a curated collection of tools and resources for web creators. It includes some tools that have been around for a while as well as some newbies. Everything is grouped and sorted by type of resources – color, icons, type, layouts, animation, shapes, docs, and miscellaneous so you can find what you are looking for faster.

Glass

Glass is a photo-sharing app for photographers. It’s a social network of sorts that lets you share images with the greater photography community without “likes.” Just great images.

Revolt

Revolt is a chat app that’s still in beta and designed for easy communication without having to download apps. It’s an open-source project that is customizable and with an intuitive and recognizable interface. The thing that’s different about this app is that it is built on a privacy-first model.

Doodle Ipsum

Doodle Ipsum is the illustrated version of placeholder elements. Customize your doodles, grab the code, and use them on your web prototypes, landing pages, or no-code tools.

Mechanic

Mechanic is an open-source framework that helps you create custom, web-based tools that export design assets in your browser. The best part is you can try it right on screen using the “poster generator.” If you like what you see, there’s plenty of documentation to help you along the way.

Medio Website Template

Medio is an agency-style website design template for Bootstrap 5. The layout is perfect for a design agency or marketing group but can be adjusted for almost any multi-purpose design. The free template includes a minimal design and includes features such as parallax, popup video, and more.

Tutorial: Simplifying Form Styles with Accent Color

This tutorial is a life-saver when it comes to using and understanding the new CSS accent-color property. This quick lesson will help make your life easier and is simple to use. It starts with setting an accent-color property on the root element and then applying it.

Houdini.how

Houdini.how is a worklet library that is full of CSS and code examples to help you work smarter. See how different elements look cross-browser and learn to adjust the code and put them together in just the way you want. Houdini is a set of low-level APIs that exposes parts of the CSS engine, giving developers the power to extend CSS by hooking into the styling and layout process of a browser’s rendering engine.

Chainstarters

Chainstarters is a powerful, rapid, Web3-enabled platform for software developers. It eliminates the burden of setting up and maintaining a secure and scalable infrastructure, allowing you to focus on creating amazing technology.

Web Vitals Robot

Web Vitals Robot is a search optimization tool that monitors SEO metrics for you – so you can prevent your business from disappearing from Google.

Searchable

Searchable is a unified search tool that looks at local, cloud storage, and apps to find the files you are looking for. It returns results in a jiffy with previews so you don’t have to open every file to find what you are looking for.

Athlone

Athlone is a fun serif with lots of personality. The free demo version includes a limited character set for personal use only and the full version has everything you need for fun display or branding with this typeface.

Capitana

Capitana is a Geometric Sans typeface with humanistic proportions and open apertures. This means that all shapes are constructed from basic forms, the circle, triangle, and square, and are designed according to the classic proportions of the Roman Antiqua. Distinct ascenders and pointed apexes with deep overshoot give it a cool beauty and classic elegance. It includes 784 characters per style in nine weights from Thin to Black, it offers both light and extremely heavy weights for striking headlines.

Colours

Colours is a funky script with just enough texture to keep it interesting. The free version includes a partial character set and is for personal use only.

Flexible

Flexible is a variable typeface that includes 18 styles in the family. It’s made for creativity and display use. This typeface is made for experimenting because there are so many things you can do with this single family.

Singo Sans Serif

Singo Sans Serif is a simple and strong typeface that would make an excellent display option. The free version is for personal use only. Fun fact: Singo means Lion in Indonesia, which is where the name of this strong font comes from.

Source

The post Exciting New Tools For Designers, October 2021 first appeared on Webdesigner Depot.

Categories: Designing, Others Tags:

Better Collaboration With Pull Requests

October 11th, 2021 No comments
Without a pull request, you would jump right to merging your code.

However, what if your changes are a bit more complex and you’d like someone else to look at your work? This is what pull requests were made for. With pull requests you can invite other people to review your work and give you feedback. 

A pull request invites reviewers to provide feedback before merging.

Once a pull request is open, you can discuss your code with other developers. Most Git hosting platforms allow other users to add comments and suggest changes during that process. After your reviewers have approved your work, it might be merged into another branch.

A pull request invites reviewers to provide feedback before merging.

Having a reviewing workflow is not the only reason for pull requests, though. They come in handy if you want to contribute to other repositories you don’t have write access to. Think of all the open source projects out there: if you have an idea for a new feature, or if you want to submit a patch, pull requests are a great way to present your ideas without having to join the project and become a main contributor.

This brings us to a topic that’s tightly connected to pull requests: forks.

Working with forks

A fork is your personal copy of an existing Git repository. Going back to our Open Source example: your first step is to create a fork of the original repository. After that, you can change code in your own, personal copy.

Creating a fork of the original respository is where you make changes.

After you’re done, you open a pull request to ask the owners of the original repository to include your changes. The owner or one of the other main contributors can review your code and then decide to include it (or not).

Two red database icons with gold arrows pointing at opposite directions between the database. The database on the left as a lock icon next to it that is circled in gold.

Important Note: Pull requests are always based on branches and not on individual commits! When you create a pull request, you base it on a certain branch and request that it gets included.

Making a reviewer’s life easier: How to create a great pull request

As mentioned earlier, pull requests are not a core Git feature. Instead, every Git platform has its own design and its own idea about how a pull request should work. They look different on GitLab, GitHub, Bitbucket, etc. Every platform has a slightly different workflow for tracking, discussing, and reviewing changes.

A layered collage of Git-based websites. Bitbucket is on top, followed by GitHub, then GitLab.

Desktop GUIs like the

This article is part of our “Advanced Git” series. Be sure to follow us on Twitter or sign up for our newsletter to hear about the next articles!

In this third installment of our “Advanced Git” series, we’ll look at pull requests — a great feature which helps both small and larger teams of developers. Pull requests not only improve the review and the feedback process, but they also help tracking and discussing code changes. Last, but not least, pull requests are the ideal way to contribute to other repositories you don’t have write access to.

Advanced Git series:

  • Part 1: Creating the Perfect Commit in Git
  • Part 2: Branching Strategies in Git
  • Part 3: Better Collaboration With Pull Requests
    You are here!
  • Part 4: Merge Conflicts
    Coming soon!
  • Part 5: Rebase vs. Merge
  • Part 6: Interactive Rebase
  • Part 7: Cherry-Picking Commits in Git
  • Part 8: Using the Reflog to Restore Lost Commits

What are pull requests?

First of all, it’s important to understand that pull requests are not a core Git feature. Instead, they are provided by the Git hosting platform you’re using: GitHub, GitLab, Bitbucket, AzureDevops and others all have such a functionality built into their platforms.

Why should I create a pull request?

Before we get into the details of how to create the perfect pull request, let’s talk about why you would want to use this feature at all.

Imagine you’ve just finished a new feature for your software. Maybe you’ve been working in a feature branch, so your next step would be merging it into the mainline branch (master or main). This is totally fine in some cases, for example, if you’re the only developer on the project or if you’re experienced enough and know for certain your team members will be happy about it.

By the way: If you want to know more about branches and typical branching workflows, have a look at our second article in our “Advanced Git” series: “Branching Strategies in Git.”

However, what if your changes are a bit more complex and you’d like someone else to look at your work? This is what pull requests were made for. With pull requests you can invite other people to review your work and give you feedback. 

A pull request invites reviewers to provide feedback before merging.

Once a pull request is open, you can discuss your code with other developers. Most Git hosting platforms allow other users to add comments and suggest changes during that process. After your reviewers have approved your work, it might be merged into another branch.

A pull request invites reviewers to provide feedback before merging.

Having a reviewing workflow is not the only reason for pull requests, though. They come in handy if you want to contribute to other repositories you don’t have write access to. Think of all the open source projects out there: if you have an idea for a new feature, or if you want to submit a patch, pull requests are a great way to present your ideas without having to join the project and become a main contributor.

This brings us to a topic that’s tightly connected to pull requests: forks.

Working with forks

A fork is your personal copy of an existing Git repository. Going back to our Open Source example: your first step is to create a fork of the original repository. After that, you can change code in your own, personal copy.

Creating a fork of the original respository is where you make changes.

After you’re done, you open a pull request to ask the owners of the original repository to include your changes. The owner or one of the other main contributors can review your code and then decide to include it (or not).

Two red database icons with gold arrows pointing at opposite directions between the database. The database on the left as a lock icon next to it that is circled in gold.

Important Note: Pull requests are always based on branches and not on individual commits! When you create a pull request, you base it on a certain branch and request that it gets included.

Making a reviewer’s life easier: How to create a great pull request

As mentioned earlier, pull requests are not a core Git feature. Instead, every Git platform has its own design and its own idea about how a pull request should work. They look different on GitLab, GitHub, Bitbucket, etc. Every platform has a slightly different workflow for tracking, discussing, and reviewing changes.

A layered collage of Git-based websites. Bitbucket is on top, followed by GitHub, then GitLab.

Desktop GUIs like the Tower Git client, for example, can make this easier: they provide a consistent user interface, no matter what code hosting service you’re using.

Animated screenshot of a pull request in the Tower application. A pull requests panel is open showing a pull request by the author that, when clicked, reveals information about that pull request on the right. The app has a dark interface.

Having said that, the general workflow is always the same and includes the following steps:

  1. If you don’t have write access to the repository in question, the first step is to create a fork, i.e. your personal version of the repository.
  2. Create a new local branch in your forked repository. (Reminder: pull requests are based on branches, not on commits!)
  3. Make some changes in your local branch and commit them.
  4. Push the changes to your own remote repository.
  5. Create a pull request with your changes and start the discussion with others.

Let’s look at the pull request itself and how to create one which makes another developer’s life easier. First of all, it should be short so it can be reviewed quickly. It’s harder to understand code when looking at 3,000 lines instead of 30 lines. 

Also, make sure to add a good and self-explanatory title and a meaningful description. Try to describe what you changed, why you opened the pull request, and how your changes affect the project. Most platforms allow you to add a screenshot which can help to demonstrate the changes.

Approve, merge, or decline?

Once your changes have been approved, you (or someone with write access) can merge the forked branch into the main branch. But what if the reviewer doesn’t want to merge the pull request in its current state? Well, you can always add new commits, and after pushing that branch, the existing pull request is updated.

Alternatively, the owner or someone else with write access can decline the pull request when they don’t want to merge the changes.

Safety net for developers

As you can see, pull requests are a great way to communicate and collaborate with your fellow developers. By asking others to review your work, you make sure that only high-quality code enters your codebase. 

If you want to dive deeper into advanced Git tools, feel free to check out my (free!) “Advanced Git Kit”: it’s a collection of short videos about topics like branching strategies, Interactive Rebase, Reflog, Submodules and much more.

Advanced Git series:

  • Part 1: Creating the Perfect Commit in Git
  • Part 2: Branching Strategies in Git
  • Part 3: Better Collaboration With Pull Requests
    You are here!
  • Part 4: Merge Conflicts
    Coming soon!
  • Part 5: Rebase vs. Merge
  • Part 6: Interactive Rebase
  • Part 7: Cherry-Picking Commits in Git
  • Part 8: Using the Reflog to Restore Lost Commits

The post Better Collaboration With Pull Requests appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Categories: Designing, Others Tags:

The Autofill Dark Pattern

October 11th, 2021 No comments

A newspaper sign-up form had fields for name, email, and password. So, I started typing on the name field, and the autofill suggested my profile. But there was something funky. The autocomplete suggestion included my mailing address. Needless to say, it was puzzling: the address was not a field in the form. Why was it even suggested?

By the time this question started forming in my mind, my brain had already signaled my finger to click on the suggestion, and it was done. Next, I was taken to a second form page which requested additional information like address, phone, date of birth, and so on. And all those fields had been pre-populated by the autofill functionality, too.

I sighed in relief. It was a “multi-stepped” form and not a trick by the website. It was a reputable newspaper, after all. I deleted all the optional information from the second page, completed the sign-up, and moved on.

That (troubled) interaction highlighted one of the risks of using autocompletion features.

Autocomplete And Autofill

They may sound similar, but autocomplete and autofill are not the same thing. Although they are closely related:

  • Autofill is a browser feature that allows people to save information (on the browser or the OS) and use it on web forms.
  • autocomplete is an HTML attribute that provides guidelines to the browser on how to (or not to) autofill in fields in a web form.

We could say that autofill is the “what,” while autocomplete the “how”, i.e. autofill stores data and tries to match it in a web form (based on the fields’ name, type, or id), and autocomplete guides the browser on how to do it (what information is expected in each field).

Autocomplete is a powerful feature with many options that allows specifying many different types of values:

  • Personal: Name, address, phone, date of birth;
  • Financial: credit card number, name, expiration date;
  • Demographics: location, age, sex, language;
  • Professional: company and job title.

Autofill is a widespread feature either by choice or by accident: who hasn’t accepted to let the browser save/use web form information, either on purpose or by mistake? And that could be a problem — especially combined with bad use of autocomplete (and the added outrageous number of phishing emails and SMS messages nowadays.)

Privacy Risks

Both of these features present (at least) two main risks for the user, both related to their personal data and its privacy:

  1. Non-visible fields are populated (this is not the same as fields with a hidden type);
  2. Autocompleted information can be read via JavaScript even before the user submits the form.

This means that once a user selects to autofill the information, all the fields will be available for the developer to read. Again, independently of the user submitting the form or not, without the user knowing what fields were actually populated.

This last part is relative: knowing what fields are populated will depend on the browser. Safari and Firefox do a good job at this (as we’ll soon see below). On the other hand, Chrome, the most popular browser at the moment, offers a bad experience that may trick even the most knowledgeable users into sharing their personal information.

If we also consider the times in which the user accidentally chooses to populate the fields, this issue becomes more relevant. Let’s check it in more detail with an example.

A Little Experiment

I ran a little experiment creating a form with many fields and attaching the autocomplete attribute with different values. Then, I played a little with the form’s structure:

  • I hid most of the fields by putting them in a container offscreen (instead of using hidden or type="hidden");
  • I removed the visually hidden fields from the tab order (so keyboard users would overlook the hidden fields);
  • I tried sorting the fields in a different order (and to my surprise, this impacted the autofill!).

In the end, the code for the form looked like this:

<form method="post" action="javascript:alertData()">
  <label for="name">Full name</label><input id="name" name="name" autocomplete="name" /><br/>
  <label for="email">Email</label><input id="email" name="email"/><br/>
  <label for="postal-code">ZIP</label><input id="postal-code" name="postal-code" autocomplete="postal-code"/>
  <div style="position:absolute;top:-10000in" class="hide-this">
    <!-- Hidden -->
    <label for="firstname">First name</label><input tabindex="-1" type="hidden" id="firstname" name="firstname" autocomplete="given-name" /><br/>
    <label for="lastname">Last name</label><input tabindex="-1"  id="lastname" name="lastname" autocomplete="family-name" /><br/>

    <label for="honorific-prefix">honorific-prefix</label><input tabindex="-1" id="honorific-prefix" name="honorific-prefix" autocomplete="honorific-prefix"/><br/>
    <label for="organization">Organization</label><input tabindex="-1" id="organization" name="organization" /><br/>
    <label for="phone">Phone</label><input tabindex="-1" id="phone" name="phone" autocomplete="tel" /><br/>

    <label for="address">address</label><input tabindex="-1" id="address" name="address" autocomplete="street-address" /><br/>
    <label for="city">City</label><input tabindex="-1" id="city" name="city" autocomplete="address-level2" /><br/>
    <label for="state">State</label><input tabindex="-1" id="state" name="state" autocomplete="address-level1" /><br/>
    <label for="level3">Level3</label><input tabindex="-1" id="level3" name="state" autocomplete="address-level3" /><br/>
    <label for="level4">Level4</label><input tabindex="-1" id="level4" name="state" autocomplete="address-level4" /><br/>
    <label for="country">Country</label><input tabindex="-1" id="country" name="country" autocomplete="country" /><br/>

    <label for="birthday">Birthday</label><input tabindex="-1" id="birthday" name="birthday" autocomplete="bday" /><br/>
    <label for="language">Language</label><input tabindex="-1" id="language" name="language" autocomplete="language" /><br/>
    <label for="sex">Sex</label><input tabindex="-1" id="sex" name="sex" autocomplete="sex" /><br/>
    <label for="url">URL</label><input tabindex="-1" id="url" name="url" autocomplete="url" /><br/>
    <label for="photo">Photo</label><input tabindex="-1" id="photo" name="photo" autocomplete="photo" /><br/>
    <label for="impp">IMPP</label><input tabindex="-1" id="impp" name="impp" autocomplete="impp" /><br/>

    <label for="username">Username</label><input tabindex="-1" id="username" name="username" autocomplete="username" /><br/>
    <label for="password">Password</label><input tabindex="-1" id="password" name="password" autocomplete="password" /><br/>
    <label for="new-password">Password New</label><input tabindex="-1" id="new-password" name="new-password" autocomplete="new-password" /><br/>
    <label for="current-password">Password Current</label><input tabindex="-1" id="current-password" name="current-password" autocomplete="current-password" /><br/>

    <label for="cc">CC#</label><input tabindex="-1" id="cc" name="cc" autocomplete="cc-number" /><br/>
    <label for="cc-name">CC Name</label><input tabindex="-1" id="cc-name" name="cc-name" autocomplete="cc-name" /><br/>
    <label for="cc-expiration">CC expiration</label><input tabindex="-1" id="cc-expiration" name="cc-expiration" autocomplete="cc-expiration" /><br/>
    <label for="cc-zipcode">CC Zipcode</label><input tabindex="-1" id="cc-zipcode" name="cc-zipcode" autocomplete="cc-postalcode" /><br/>
  </div>
  <button>Submit</button>
</form>

Note: I created this demo a while back, and the standard is a living document. Since then, some of the autocomplete names have changed. For example, now we can specify new-password and current-password or more details for address or credit card that were not available before.

That form had three visible fields (name, email and zipcode). While that form is common among insurance companies, cable, and other service providers, it may not be too widespread, so I reduced the form even more with a single email field. We see that everywhere to sign up to websites, newsletters, or updates. You can see a running demo here:

A good thing: it displays all the data that will be shared as part of the form. Not only the data for the visible fields but all of them. At this point, the user may suspect something is not quite alright. There’s something fishy.

When I reduced the form to just the email field, Safari did something interesting. The autofill popup was different:

It states that it will only share the email (and it only does share that piece of information). But the contact info below may be trickier. When we click on that button, the browser shows a summary of the profile with its shared data. But that is not clearly stated anywhere. It simply looks like a regular contact card with some “share/do not share” options. After clicking on the “Autofill” button, the form is populated with all the data. Not only the email:

So there is a way for a user to share information with the form inadvertently. It’s tricky but not too far-fetched considering that it is the one “highlighted” with an icon out of the two possible options.

Funny thing, browsers separate the personal data from the credit card data, but Safari populated part of the credit card information based on the personal data (name and ZIP.)

Firefox

Using the autofill in Firefox is a bit more complex. It is not automatic like in Chrome, and there’s no icon like in Safari. Users will have to start typing or click a second time to see the autofill popup, which will have a note with every category that the browser will fill in, not only the visible fields:

Testing with the email-only form, Firefox presented the same autofill popup stating which fields categories it would populate. No difference whatsoever.

And similarly to the other browsers, after the autofill ran, we could read all the values with JavaScript.

Firefox was the best of the three: it clearly stated what information would be shared with the form independently of the fields or their order. And it hid the autofill functionality a second user interaction happened.

A keyboard user could select the autofill without realizing, by getting inside the popup bubble and pressing the tab key.

Chrome

Then it came the turn for Chrome. (Here I use “Chrome,” but the results were similar for several Chromium-based browsers tested.) I clicked on the field and, without any further interaction, the autofill popup showed. While Firefox and Safari had many things in common, Chrome is entirely different: it only shows two values, and both are visible.

This display was by design. I picked the order of the fields on purpose to get that particular combination of visible controls and autocomplete suggestions. However, it looks like Chrome gives some autocomplete properties more “weight” for the second value. And that makes the popup change depending on the order of the fields in the form.

Testing with the second version of the form was not much better:

While the popup shows a field that is not visible (the name), it is unclear what the purpose of the name is on the popup. An experienced user may know this happens because the name is shared, but an average user (and even the experienced ones) may think the email is associated with the profile with that name. There is zero indication of the data that the browser will share with the form.

And as soon as the user clicks on the autofill button, the data is available for the developer to read with JavaScript:

Chrome was the worst offender: it shared the information automatically, it was unclear what data was involved, and the autofill suggestions changed based on the controls’ order and attributes.

The first two issues are common to all/many browsers, to the point that it may even be considered a feature. However, the third issue is exclusive to Chromium browsers, and it facilitates a sketchy dark pattern.

This behavior would be more of an anecdote and not a problem if it wasn’t because Chrome takes a considerable market share of the browsers online (includes Chrome and Chromium-based).

The Dark Pattern

As you probably know, a dark pattern is a deceptive UX pattern that tricks users into doing things they may not really want to do.

“When you use websites and apps, you don’t read every word on every page — you skim read and make assumptions. If a company wants to trick you into doing something, they can take advantage of this by making a page look like it is saying one thing when it is in fact saying another.”

— Harry Brignull, darkpatterns.org

The behavior described in the previous points is clearly a deceptive user experience. Non-experienced users will not realize that they are sharing their personal data. Even more tech-savvy people may get tricked by it as Chrome makes it look like the selected option belongs to a profile instead of clearly stating what information is being shared.

The browser implementations cause this behavior, but it requires the developer to set it in place to exploit it. Unfortunately, there already are companies willing to exploit it, selling it as a feature to get leads.

As long as a dark pattern goes, it may also be an illegal one. This is because it breaks many principles relating to the processing of personal data specified in article 5 of the European General Data Protection Regulation (GDPR):

  • Lawfulness, fairness, and transparency
    The process is all but transparent.
  • Purpose limitation
    The data is processed in a way incompatible with the initial purpose.
  • Data minimization
    It is quite the opposite. Data maximization: get as much information as possible.

For example, if you want to sign up for a newsletter or request information about a product, and you provide your email, the website has no legal right to get your name, address, date of birth, phone number, or anything else without your consent or knowledge. Even if you considered that the user gave permission when clicking on the autofill, the purpose of the obtained data does not match the original intent of the form.

Possible Solutions

To avoid the problem, all actors need to contribute and help fix the issue:

  1. Users
  2. Developers and designers
  3. Browsers

1. Users

The only thing on the user side would be to ensure that the data displayed in the autofill popup is correct.

But we need to remember that the user is the victim here. We could blame them for not paying enough attention when clicking on the autofill, but that would be unfair. Plus, there are many reasons why a person could click on the button by mistake and share their data by accident. So even well-intentioned and savvy users may fall for it.

2. Developers And Designers

Let’s be honest. While the developers are not the root cause of the problem, they play a key role in exploiting the dark pattern. Either accidentally or with malicious intent.

And let’s be responsible and honest (this time in a literal way), because that’s the thing that developers and designers can do to build trust and make good use of the autofill and autocomplete features:

  • Only auto-complete the data that you need.
  • State clearly which data will be collected.
  • Do not hide form fields that will be later submitted.
  • Do not mislead or trick users into sending more data.

As an extreme measure, maybe try to avoid autocompleting certain fields. But, of course, this brings other issues as it will make the form less usable and accessible. So finding a balance may be tricky.

All this is without considering the possibility of an XSS vulnerability that could exploit the dark pattern. Of course, that would be a completely different story and an even more significant problem.

3. Browsers

Much of the work would need to be done from the browser side (especially on the Chromium side). But let me start by stating that not all is bad with how web browsers handle autofill/autocomplete. Many things are good. For example:

  • They limit the data that can be shared
    Browsers have a list of fields for auto-complete that may not include all the values described in the HTML standard.
  • They encapsulate and group data
    Browsers separate personal and financial information to protect critical values like credit cards. Safari had some issues with this, but it was minor.
  • They warn about the data that will be shared
    Sometimes this may be incomplete (Chrome) or not clear (Safari), but they do alert the user.

Still, some things can be improved by many or all of the web browsers.

Show All Fields That Will Be Autocompleted

Browsers should always show a list of all the fields that will be autocompleted within the autofill popup (instead of just a partial list.) Also, the information should be clearly identified as data to be shared instead of being displayed as a regular contact card that could be misleading.

Firefox did an excellent job at this point, Safari did a nice job in general, and Chrome was subpar compared to the other two.

Do Not Trigger The onChange Event On Autofill

This would be a problematic request because this behavior is part of the Autofill definition in the HTML standard:

“The autocompletion mechanism must be implemented by the user agent acting as if the user had modified the control’s data […].”

This means that browsers should treat the autocompleted data as if it had been entered by the user, thus triggering all the events, showing the values, etc. Even on a non-visually available field.

Preventing this behavior on non-visible elements could solve the problem. But validating if a form control is visible or not could be costly for the browser. Also, this solution is only partial because developers could still read the values even without the inputs triggering events.

Do Not Allow Devs To Read Autocompleted Fields Before Submission

This would also be problematic because many developers often rely on reading the field values before submission to validate the values (e.g., when the user moves away from the inputs.) But it would make sense: the user doesn’t want to share the information until they submit the form, so the browser shouldn’t either.

An alternative to this would be providing fake data when reading autocompleted values. Web browsers already do something like this with visited links, why not do the same with autocompleted form fields? Provide gibberish as the name, a valid address that matches local authorities instead of the user’s address, a fake phone number? This could solve the developer validation needs while protecting the user’s personal information.

Displaying a complete list of the fields/values that the browser will clearly share with the form would be a great step forward. The other two are ideal but more of stretch goals. Still, they are initiatives that would considerably improve privacy.

Would the autofill dark pattern still be possible to exploit? Unfortunately, yes. But it would be a lot more complicated. And at this point, it would be the user’s responsibility and the developer’s duty to avoid such a situation.

Conclusion

We can argue that autocomplete is not a huge security issue (not even on Chrome) because it requires user interaction to select the information. However, we could also argue that the potential loss of data justifies proper action. And Chrome has done more changes for (relatively) less important security/usability concerns (see alert(), prompt(), and confirm() than what could be done to protect key personal information.

Then we have the issue of the dark pattern. It can be avoided if everyone does their part:

  • Users should be careful with what forms/data they autofill;
  • Developers should avoid exploiting that data;
  • Browsers should do a better job at protecting people’s data.

At the root, this dark pattern is a browser issue (and mainly a Chrome issue), and not a small one (privacy should be key online). But there is a choice. In the end, exploiting the dark pattern or not is up to the developers. So let’s pick wisely and do the right thing.

Further Reading On Smashing Magazine

Categories: Others Tags:

Popular Design News of the Week: October 4, 2021 – October 10, 2021

October 10th, 2021 No comments

Every day design fans submit incredible industry stories to our sister-site, Webdesigner News. Our colleagues sift through it, selecting the very best stories from the design, UX, tech, and development worlds and posting them live on the site.

The best way to keep up with the most important stories for web professionals is to subscribe to Webdesigner News or check out the site regularly. However, in case you missed a day this week, here’s a handy compilation of the top curated stories from the last seven days. Enjoy!

How to Find Business Ideas

What Happened to Treehouse?

Coca-Cola Tweaks Brand with Magical New Logo – And It’s Genius

Awesome CSS3 Background Gradients

18 Essential Graphic Design Terms You Need to Know

Windows 11 Review: An Unnecessary Replacement for Windows 10

3 Essential Design Trends, October 2021

12 Helpful Chrome Extensions for Web Designers & Developers

An Exploration of the 80s Retro Style in Modern Web Design

The Rise of Dark Web Design

Source

The post Popular Design News of the Week: October 4, 2021 – October 10, 2021 first appeared on Webdesigner Depot.

Categories: Designing, Others Tags:

Composable CSS Animation In Vue With AnimXYZ

October 9th, 2021 No comments

In this article, you will learn how to use the AnimXYZ toolkit to create unique, interactive, and visually engaging animations in Vue.js and plain HTML. By the end of this article, you will have learned how adding a few CSS classes to elements in Vue.js components can give you a lot of control over how those elements move in the DOM.

This tutorial will be beneficial to readers who are interested in creating interactive animations with few lines of code.

Note: This article requires a basic understanding of Vue.js and CSS.

What Is AnimXYZ?

AnimXYZ is a composable, performant, and customizable CSS animation toolkit powered by CSS variables. It is designed to enable you to create awesome and unique animations without writing a line of CSS keyframes. Under the hood, it uses CSS variables to create custom CSS properties. The nice thing about AnymXYZ is its declarative approach. An element can be animated in one of two ways: when entering or leaving the page. If you want to animate an HTML element with this toolkit, adding a class of xyz-out will animate the item out of the page, while xyz-in will animate the component into the page.

This awesome toolkit can be used in a regular HTML project, as well as in a Vue.js or React app. However, as of the time of writing, support for React is still under development.

Why Use AnimXYZ?

Composable

Animation with AnimXYZ is possible by adding descriptive class names to your markup. This makes it easy to write complex CSS animation without writing complex CSS keyframes. Animating an element into the page is as easy as adding a class of xyz-in in the component and declaring a descriptive attribute.

<p class="xyz-in" xyz="fade">Composable CSS animation with AnimXYZ</p>

The code above will make the paragraph element fade into the page, while the code below will make the element fade out of the page. Just a single class with a lot of power.

<p class="intro xyz-out" xyz="fade">Composable CSS animation with AnimXYZ</p>

Customizable

For simple animations, you can use the out-of-the-box utilities, but AnimXYZ can do so much more. You can customize and control AnimXYZ to create exactly the animations you want by setting the CSS variables that drive all AnimXYZ animations. We will create some custom animations later on in this tutorial.

Performant

With AnimXYZ, you can create powerful and smooth animations out of the box, and its size is only 2.68 KB for the base functionality and 11.4 KB if you include the convenient utilities.

Easy to Learn and Use

AnimXYZ works perfectly with regular HTML and CSS, and it can be integrated in a project using the content delivery network (CDN) link. It can also be used in Vue.js and React, although support for React is still under development. Also, the learning curve with this toolkit is not steep when compared to animation libraries such as GSAP and Framer Motion, and the official documentation makes it easy to get started because it explains how the package works in simple terms.

Key Concepts in AnimXYZ

Contexts

When you want a particular flow of animation to be applied to related groups of element, the xyz attribute provides the context. Let’s say you want three divs to be animated in the same way when they enter the page. All you have to do is add the xyz attribute to the parent element, with the composable utilities and variable that you want to apply.

<div class="shape-wrapper xyz-in" xyz="fade flip-up flip-left">
  <div class="shape"></div>
  <div class="shape"></div>
  <div class="shape"></div>
</div>

The code above will apply the same animation to all divs with a class of shape. All child elements will fade into the page and flip to the upper left, because the attribute xyz="fade flip-up flip-left" has been applied to the parent element.

See the Pen Contexts in AnimXYZ by Ejiro Asiuwhu.

AnimXYZ makes it easy to animate a child element differently from its parent. To achieve this, add the xyz attribute with a different animation variable and different utilities to the child element, which will reset all animation properties that it has inherited from its parent.

See the Pen Override Parent contexts in AnimXYZ by Ejiro Asiuwhu.

Utilities

AnimXYZ comes with a lot of utilities that will enable you to create engaging and powerful CSS animations without writing any custom CSS.

xyz="fade up in-left in-rotate-left out-right out-rotate-right"

For example, the code above has a fade up utility, which will make the element fade from top to bottom when coming into the page. It will come in and rotate from the left. When the element leaves the page, it will go to the right and rotate out of the page.

With the out-of-the-box utilities, you can, say, flip a group of elements to the right and make them fade while leaving the page. The possibilities of what can be achieved with the utilities are endless.

Staggering

The stagger utility controls the animation-delay CSS property for each of the elements in a list, so that their animations are triggered one after another. It specifies the amount of time to wait between applying the animation to an element and beginning to perform the animation. Essentially, it is used to queue up animation so that elements can be animated in sequence.

<div class="shape-wrapper" xyz="fade up-100% origin-top flip-down flip-right-50% rotate-left-100% stagger">
  <div class="shape xyz-in"></div>
  <div class="shape xyz-in"></div>
  <div class="shape xyz-in"></div>
  <div class="shape xyz-in"></div>
</div>

By adding the stagger utility, each element in a parent div will animate one after another from left to right. The order can be revered by using stagger-rev.

With stagger:

See the Pen Staggering with AnimXYZ by Ejiro Asiuwhu.

Without stagger:

See the Pen !Staggering Animation – AnimXYZ by Ejiro Asiuwhu.

Using AnimXYZ With HTML and CSS

Let’s build a card and add some cool animation with AnimeXYZ.

See the Pen Animxyz Demo by Ejiro Asiuwhu.

First, we need to add the AnimXYZ toolkit to our project. The easiest way is via a CDN. Grab the CDN, and add it to the head of your HTML document.

Add the following lines of code to your HTML.

<p class="intro xyz-in" xyz="fade">Composable CSS Animation with Animxyz</p>
  <div class="glass xyz-in" id="glass"
        xyz="fade flip-down flip-right-50%  duration-10">
        <img src="https://cdn.dribbble.com/users/238864/screenshots/15043450/media/7160a9f4acc18f4ec2cbe38eb167de62.jpg"
            alt="" class="avatar xyz-in">
        <p class="des xyz-in">Image by Jordon Cheung</p>
  </div>

This is where the magic happens. At the top of the page, we have a paragraph tag with a class of xyz-in and an xyz attribute with a value of fade. This means that the p element will fade into the page.

Next, we have a card with an id of glass, with the following xyz attribute:

  xyz="fade flip-down flip-right-50%  duration-10"

The composable utilities above will make the card fade into the page. The flip-down value will set the card to flip into the page from the bottom, and the flip-right value will flip the card by 50% when leaving the page. An animation duration of 10 (i.e. 1 second) sets the length of time that the animation will take to complete one cycle.

Integrating AnimXYZ in Vue.js

Scaffold a Vue.js Project

Using the Vue.js command-line interface (CLI), run the command below to generate the application:

vue create animxyz-vue

Install VueAnimXYZ

npm install @animxyz/vue

This will install both the core package and the Vue.js package. After installation, we will have to import the VueAnimXYZ package into our project and add the plugin globally to our Vue.js application. To do this, open your main.js file, and add the following block of code accordingly:

import VueAnimXYZ from '@animxyz/vue'  // import AnimXZY vue package
import '@animxyz/core' // import AnimXZY core package

Vue.use(VueAnimXYZ)

The XyzTransition Component

The XyzTransition component is built on top of Vue.js’ transition component. It’s used to animate individual elements into and out of the page.

Here is a demo of how to use the XyzTransition component in Vue.js.

Notice that a lot of the complexity that comes with Vue.js’ transition component has been abstracted away in order to reduce complexity and increase efficiency. All we need to care about when using the XyzTransition component are the appear, appear-visible, duration, and mode props.

For a more detailed guide, check out the official documentation.

Let’s use the XYZTransition component to animate an element when a button is clicked.

<div id="app">
    <button @click="isAnimate = !isAnimate">Animate</button>
    <XyzTransition
      appear
      xyz="fade up in-left in-rotate-left out-right out-rotate-right"
    >
      <div class="square" v-if="isAnimate"></div>
    </XyzTransition>
</div>

Notice how the element that we intend to transition is wrapped in the XYZTransition component. This is important because the child element

will inherit the utilities that are applied to the XYZTransition component. The child element is also conditionally rendered when isAnimate is set to true. When the button is clicked, the child element with a class of square is toggled into and out of the DOM.

XyzTransitionGroup

The XyzTransitionGroup component is built on top of Vue.js’ transition-group component. It is used to animate groups of elements into and out of the page.

Below is an illustration of how to use the XyzTransitionGroup component in Vue.js. Notice here again that a lot of the complexity that comes with Vue.js’ transition-group component has been abstracted away in order to reduce complexity and increase efficiency. All we need to care about when using the XyzTransitionGroup component are appear, appear-visible, duration, and tag. The following is taken from the documentation:

<XyzTransitionGroup
  appear={ boolean }
  appear-visible={ boolean | IntersectionObserverOptions }
        duration={ number | 'auto' | { appear: number | 'auto', in: number | 'auto',
                   out: number | 'auto' } }
        tag={ string } >
        <child-component />
        <child-component />
        <child-component />
</XyzTransitionGroup>

Build an Animated Modal With AnimXYZ and Vue.js

Let’s build modal components that will animate as they enter and leave the DOM.

Here is a demo of what we are going to build:

Demo

By adding the xyz="fade out-delay-5" property to the XyzTransition component, the modal will fade.

Notice that we are adding .xyz-nested to almost all child elements of the modal component. This is because we want to trigger their animations when a modal component’s element is open.

The ease-out-back property that we added to the dialog container will add a slight overshoot when the dialog is opened and when it has been closed.

Adding in-delay to the child elements of the modal component will make the animation feel more natural, because the element will be delayed until the other contents of the modal have animated in:

  <section class="xyz-animate">
    <div class="alerts__wrap copy-content">
      <div class="alert reduced-motion-alert">
        <p>
          AnimXYZ animations are disabled if your browser or OS has
          reduced-motion setting turned on.
          <a href="https://web.dev/prefers-reduced-motion/" target="_blank">
            Learn more here.
          </a>
        </p>
      </div>
    </div>
    <h1>Modal Animation With AnimXYZ and Vue.js</h1>
    <button
      class="modal-toggle modal-btn-main"
      data-modal-text="Open Modal"
      data-modal-title="Title"
      data-modal-close-text="Close"
      id="label_modal_kdf8e87cga"
      aria-haspopup="dialog"
      ref="openButton"
      @click="open"
      autofocus
    >
      Open Modal
    </button>
    <span
      id="js-modal-overlay"
      class="simple-modal-overlay"
      data-background-click="enabled"
      title="Close this window"
      v-if="isModal"
      @click="close"
    >
      <span class="invisible">Close this window</span>
    </span>
    <div
      role="dialog"
      class="simple-modal__wrapper"
      aria-labelledby="modal-title"
    >
      <XyzTransition duration="auto" xyz="fade out-delay-5">
        <section
          id="modal1"
          aria-labelledby="modal1_label"
          aria-modal="true"
          class="modal xyz-nested"
          xyz="fade small stagger ease-out-back"
          v-if="isModal"
          tabindex="-1"
          ref="modal"
          @keydown.esc="close"
        >
          <div class="modal_top flex xyz-nested" xyz="up-100% in-delay-3">
            <header
              id="modal1_label modal-title"
              class="modal_label xyz-nested"
              xyz="fade right in-delay-7"
            >
              Join our community on Slack
            </header>
            <button
              type="button"
              aria-label="Close"
              xyz="fade small in-delay-7"
              class="xyz-nested"
              @click="close"
              title="Close"
            >
              <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true">
                <path
                  fill="currentColor"
                  d="M.439,21.44a1.5,1.5,0,0,0,2.122,2.121L11.823,14.3a.25.25,0,0,1,.354,0l9.262,9.263a1.5,1.5,0,1,0,2.122-2.121L14.3,12.177a.25.25,0,0,1,0-.354l9.263-9.262A1.5,1.5,0,0,0,21.439.44L12.177,9.7a.25.25,0,0,1-.354,0L2.561.44A1.5,1.5,0,0,0,.439,2.561L9.7,11.823a.25.25,0,0,1,0,.354Z"
                ></path>
              </svg>
            </button>
          </div>
          <div class="modal_body xyz-nested" xyz="up-100% in-delay-3">
            <div class="modal_body--top flex justify_center align_center">
              <img
                src="../assets/slack.png"
                alt="slack logo"
                class="slack_logo"
              />
              <img src="../assets/plus.png" alt="plus" class="plus" />
              <img
                src="../assets/discord.png"
                alt="discord logo"
                class="discord_logo"
              />
            </div>
            <p><span class="bold">929</span> users are registered so far.</p>
          </div>
          <form class="modal_form" autocomplete>
            <label for="email"
              ><span class="sr-only">Enter your email</span></label
            >
            <input
              id="email"
              type="email"
              placeholder="johndoe@email.com"
              autocomplete="email"
              aria-describedby="email"
              class="modal_input"
              required
            />
            <button type="submit" class="modal_invite_btn">
              Get my invite
            </button>
            <p>Already joined?</p>
            <button
              type="button"
              aria-describedby="open_slack"
              class="
                modal_slack_btn
                flex
                align_center
                justify_center
                xyz-nested
              "
              xyz="fade in-right in-delay-7"
              id="open_slack"
            >
              <span
                ><img src="../assets/slack.png" alt="slack logo" role="icon"
              /></span>
              Open Slack
            </button>
          </form>
        </section>
      </XyzTransition>
    </div>
  </section>

Then, in our modal, we would use the v-if="isModal" directive to specify that we want the modal to be hidden from the page by default. Then, when the button is clicked, we open the modal by calling the open() method, which sets the isModal property to true. This will reveal the modal on the page and also apply the animation properties that we specified using AnimXYZ’s built-in utilities.

<script>
export default {
  data() {
    return {
      isModal: false,
    };
  },
  methods: {
    open() {
      if (!this.isModal) {
        this.isModal = true;
        this.$nextTick(() => {
          const modalRef = this.$refs.modal;
          console.log(modalRef);
          modalRef.focus();
        });
      }
    },
    close() {
      if (this.isModal) {
        this.isModal = false;
        this.$nextTick(() => {
          const openButtonRef = this.$refs.openButton;
          openButtonRef.focus();
        });
      }
    },
  },
};
</script>

AnimXYZ’s animations are disabled when the reduced-motion setting in the browser or operating system is turned on. Let’s display a helper message to users who have opted in to reduced motion.

Using the @media screen and (prefers-reduced-motion) media query, we’ll display a message notifying those users that they’ve turned off the animation feature in our modal component. To do this, add the following block of code to your styles:

<style>
@media (prefers-reduced-motion: reduce) {
  .alerts__wrap {
    display: block;
  }
}
</style>

AnimXYZ animations when reduced-motion setting is turned on.

Conclusion

We’ve gone through the basics of AnimXYZ and how to use it with plain HTML and Vue.js. We’ve also implemented some demo projects that give us a glimpse of the range of CSS animations that we can create simply by adding the composable utility classes provided by this toolkit, and all without writing a single line of a CSS keyframe. Hopefully, this tutorial has given you a solid foundation to add some sleek CSS animations to your own projects and to build on them over time for any of your needs.

The final demo is on GitHub. Feel free to clone it and try out the toolkit for yourself.

That’s all for now! Let me know in the comments section below what you think of this article. I am active on Twitter and GitHub. Thank you for reading, and stay tuned.

Resources

Categories: Others Tags:

The Case for ‘Developer Experience’

October 8th, 2021 No comments

A good essay from Jean Yang.

What I mean by developer experience is the sum total of how developers interface with their tools, end-to-end, day-in and day-out. Sure, there’s more focus than ever on how developers use and adopt tools, and there are entire talks and panels devoted to the topic of so-called “DX” — yet large parts of developer experience are still largely ignored. With developers spending less than a third of their time actually writing code, developer experience includes all the other stuff: maintaining code, testing, security issues, addressing incidents, and more. And many of these aspects of developer experience continue getting ignored because they’re complex, they’re messy, and they don’t have “silver bullet” solutions.

She makes the case that DX has perhaps been generally oversimplified and there are categories of tools that have very different DX:

My major revelation was that there are actually two categories of tools — and therefore, two different categories of developer experience needs: abstraction tools (which assume we code in a vacuum) and complexity-exploring tools (which assume we work in complex environments). Most developer experience until now has been solely focused on the former category of abstraction, where there are more straightforward ways to understand good developer experience than the former. 

Reminds me of how Shawn thinks:

It’s time we look beyond the easy questions in developer experience and start addressing the uncomfortable ones.

Direct Link to ArticlePermalink


The post The Case for ‘Developer Experience’ appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Categories: Designing, Others Tags:

Jekyll doesn’t do components? Liar!

October 8th, 2021 No comments

I like the pushback from Katie Kodes here. I’ve said in the past that I don’t think server-side languages haven’t quite nailed “building in components” as well as JavaScript has, but hey, this is a good point:

1. Any basic fragment-of-HTML “component” you can define with JSX in a file and then cross-reference as , you can just as easily define with Liquid in a file and cross-reference in Jekyll as {% include MyComponent.html key=value %}.

2. Any basic fragment-of-HTML “layout” you can define with JSX in a file and then cross-reference as Hello, world, you can just as easily define with Liquid in a file and then cross-reference in the front matter of a Jekyll template as layout: MyLayout.

Any HTML preprocessor that can do partials with local variables is pretty close to replicating the best of stateless JavaScript components. The line gets even blurrier with stuff like Eleventy Serverless that can build individual pages on the fly by hitting the URL of a cloud function.

Direct Link to ArticlePermalink


The post Jekyll doesn’t do components? Liar! appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Categories: Designing, Others Tags:

Building a Tennis Trivia App With Next.js and Netlify

October 8th, 2021 No comments

Today we will be learning how to build a tennis trivia app using Next.js and Netlify. This technology stack has become my go-to on many projects. It allows for rapid development and easy deployment.

Without further ado let’s jump in!

What we’re using

  • Next.js
  • Netlify
  • TypeScript
  • Tailwind CSS

Why Next.js and Netlify

You may think that this is a simple app that might not require a React framework. The truth is that Next.js gives me a ton of features out of the box that allow me to just start coding the main part of my app. Things like webpack configuration, getServerSideProps, and Netlify’s automatic creation of serverless functions are a few examples.

Netlify also makes deploying a Next.js git repo super easy. More on the deployment a bit later on.

What we’re building

Basically, we are going to build a trivia game that randomly shows you the name of a tennis player and you have to guess what country they are from. It consists of five rounds and keeps a running score of how many you get correct.

The data we need for this application is a list of players along with their country. Initially, I was thinking of querying some live API, but on second thought, decided to just use a local JSON file. I took a snapshot from RapidAPI and have included it in the starter repo.

The final product looks something like this:

You can find the final deployed version on Netlify.

Starter repo tour

If you want to follow along you can clone this repository and then go to the start branch:

git clone git@github.com:brenelz/tennis-trivia.git
cd tennis-trivia
git checkout start

In this starter repo, I went ahead and wrote some boilerplate to get things going. I created a Next.js app using the command npx create-next-app tennis-trivia. I then proceeded to manually change a couple JavaScript files to .ts and .tsx. Surprisingly, Next.js automatically picked up that I wanted to use TypeScript. It was too easy! I also went ahead and configured Tailwind CSS using this article as a guide.

Enough talk, let’s code!

Initial setup

The first step is setting up environment variables. For local development, we do this with a .env.local file. You can copy the .env.sample from the starter repo.

cp .env.sample .env.local

Notice it currently has one value, which is the path of our application. We will use this on the front end of our app, so we must prefix it with NEXT_PUBLIC_.

Finally, let’s use the following commands to install the dependencies and start the dev server: 

npm install
npm run dev

Now we access our application at http://localhost:3000. We should see a fairly empty page with just a headline:

Creating the UI markup

In pages/index.tsx, let’s add the following markup to the existing Home() function:

export default function Home() {
  return (
    <div className="bg-blue-500">
    <div className="max-w-2xl mx-auto text-center py-16 px-4 sm:py-20 sm:px-6 lg:px-8">
      <h2 className="text-3xl font-extrabold text-white sm:text-4xl">
        <span className="block">Tennis Trivia - Next.js Netlify</span>
      </h2>
      <div>
        <p className="mt-4 text-lg leading-6 text-blue-200">
          What country is the following tennis player from?
        </p>
        <h2 className="text-lg font-extrabold text-white my-5">
          Roger Federer
        </h2>

        <form>
          <input
            list="countries"
            type="text"
            className="p-2 outline-none"
            placeholder="Choose Country"
          />
          <datalist id="countries">
            <option>Switzerland</option>
           </datalist>
           <p>
             <button
               className="mt-8 w-full inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-blue-600 bg-white hover:bg-blue-50 sm:w-auto"
               type="submit"
             >
               Guess
            </button>
          </p>
        </form>

        <p className="mt-4 text-lg leading-6 text-white">
          <strong>Current score:</strong> 0
        </p>
      </div>
    </div>
    </div>
  );

This forms the scaffold for our UI. As you can see, we are using lots of utility classes from Tailwind CSS to make things look a little prettier. We also have a simple autocomplete input and a submit button. This is where you will select the country you think the player is from and then hit the button. Lastly, at the bottom, there is a score that changes based on correct or incorrect answers.

Setting up our data

If you take a look at the data folder, there should be a tennisPlayers.json with all the data we will need for this application. Create a lib folder at the root and, inside of it, create a players.ts file. Remember, the .ts extension is required since is a TypeScript file. Let’s define a type that matches our JSON data..

export type Player = {
  id: number,
  first_name: string,
  last_name: string,
  full_name: string,
  country: string,
  ranking: number,
  movement: string,
  ranking_points: number,
};

This is how we create a type in TypeScript. We have the name of the property on the left, and the type it is on the right. They can be basic types, or even other types themselves.

From here, let’s create specific variables that represent our data:

export const playerData: Player[] = require("../data/tennisPlayers.json");
export const top100Players = playerData.slice(0, 100);

const allCountries = playerData.map((player) => player.country).sort();
export const uniqueCountries = [...Array.from(new Set(allCountries))];

A couple things to note is that we are saying our playerData is an array of Player types. This is denoted by the colon followed by the type. In fact, if we hover over the playerData we can see its type:

In that last line we are getting a unique list of countries to use in our country dropdown. We pass our countries into a JavaScript Set, which gets rid of the duplicate values. We then create an array from it, and spread it into a new array. It may seem unnecessary but this was done to make TypeScript happy.

Believe it or not, that is really all the data we need for our application!

Let’s make our UI dynamic!

All our values are hardcoded currently, but let’s change that. The dynamic pieces are the tennis player’s name, the list of countries, and the score.

Back in pages/index.tsx, let’s modify our getServerSideProps function to create a list of five random players as well as pull in our uniqueCountries variable.

import { Player, uniqueCountries, top100Players } from "../lib/players";
...
export async function getServerSideProps() {
  const randomizedPlayers = top100Players.sort((a, b) => 0.5 - Math.random());
  const players = randomizedPlayers.slice(0, 5);

  return {
    props: {
      players,
      countries: uniqueCountries,
    },
  };
}

Whatever is in the props object we return will be passed to our React component. Let’s use them on our page:

type HomeProps = {
  players: Player[];
  countries: string[];
};

export default function Home({ players, countries }: HomeProps) {
  const player = players[0];
  ...
} 

As you can see, we define another type for our page component. Then we add the HomeProps type to the Home() function. We have again specified that players is an array of the Player type.

Now we can use these props further down in our UI. Replace “Roger Federer” with {player.full_name} (he’s my favorite tennis player by the way). You should be getting nice autocompletion on the player variable as it lists all the property names we have access to because of the types that we defined.

Further down from this, let’s now update the list of countries to this:

<datalist id="countries">
  {countries.map((country, i) => (
    <option key={i}>{country}</option>
  ))}
</datalist>

Now that we have two of the three dynamic pieces in place, we need to tackle the score. Specifically, we need to create a piece of state for the current score.

export default function Home({ players, countries }: HomeProps) {
  const [score, setScore] = useState(0);
  ...
}

Once this is done, replace the 0 with {score} in our UI.

You can now check our progress by going to http://localhost:3000. You can see that every time the page refreshes, we get a new name; and when typing in the input field, it lists all of the available unique countries.

Adding some interactivity

We’ve come a decent way but we need to add some interactivity.

Hooking up the guess button

For this we need to have some way of knowing what country was picked. We do this by adding some more state and attaching it to our input field.

export default function Home({ players, countries }: HomeProps) {
  const [score, setScore] = useState(0);
  const [pickedCountry, setPickedCountry] = useState("");
  ...
  return (
    ...
    <input
      list="countries"
      type="text"
      value={pickedCountry}
      onChange={(e) => setPickedCountry(e.target.value)}
      className="p-2 outline-none"
      placeholder="Choose Country"
    />
   ...
  );
}

Next, let’s add a guessCountry function and attach it to the form submission:

const guessCountry = () => {
  if (player.country.toLowerCase() === pickedCountry.toLowerCase()) {
    setScore(score + 1);
  } else {
    alert(‘incorrect’);
  }
};
...
<form
  onSubmit={(e) => {
    e.preventDefault();
    guessCountry();
  }}
>

All we do is basically compare the current player’s country to the guessed country. Now, when we go back to the app and guess the country right, the score increases as expected.

Adding a status indicator

To make this a bit nicer, we can render some UI depending whether the guess is correct or not.

So, let’s create another piece of state for status, and update the guess country method:

const [status, setStatus] = useState(null);
...
const guessCountry = () => {
  if (player.country.toLowerCase() === pickedCountry.toLowerCase()) {
    setStatus({ status: "correct", country: player.country });
    setScore(score + 1);
  } else {
    setStatus({ status: "incorrect", country: player.country });
  }
};

Then render this UI below the player name:

{status && (
  <div className="mt-4 text-lg leading-6 text-white">
    <p>      
      You are {status.status}. It is {status.country}
    </p>
    <p>
      <button
        autoFocus
        className="outline-none mt-8 w-full inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-blue-600 bg-white hover:bg-blue-50 sm:w-auto"
      >
        Next Player
      </button>
    </p>
  </div>
)}

Lastly, we want to make sure our input field doesn’t show when we are in a correct or incorrect status. We achieve this by wrapping the form with the following:

{!status && (
  <form>
  ...
  </form>
)}

Now, if we go back to the app and guess the player’s country, we get a nice message with the result of the guess.

Progressing through players

Now probably comes the most challenging part: How do we go from one player to the next?

First thing we need to do is store the currentStep in state so that we can update it with a number from 0 to 4. Then, when it hits 5, we want to show a completed state since the trivia game is over.

Once again, let’s add the following state variables:

const [currentStep, setCurrentStep] = useState(0);
const [playersData, setPlayersData] = useState(players);

…then replace our previous player variable with:

const player = playersData[currentStep];

Next, we create a nextStep function and hook it up to the UI:

const nextStep = () => {
  setPickedCountry("");
  setCurrentStep(currentStep + 1);
  setStatus(null);
};
...
<button
  autoFocus
  onClick={nextStep}
  className="outline-none mt-8 w-full inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-blue-600 bg-white hover:bg-blue-50 sm:w-auto"
 > 
   Next Player
</button>

Now, when we make a guess and hit the next step button, we’re taken to a new tennis player. Guess again and we see the next, and so on. 

What happens when we hit next on the last player? Right now, we get an error. Let’s fix that by adding a conditional that represents that the game has been completed. This happens when the player variable is undefined.

{player ? (
  <div>
    <p className="mt-4 text-lg leading-6 text-blue-200">
      What country is the following tennis player from?
    </p>
    ...
    <p className="mt-4 text-lg leading-6 text-white">
      <strong>Current score:</strong> {score}
    </p>
  </div>
) : (
  <div>
    <button
      autoFocus
      className="outline-none mt-8 w-full inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-indigo-600 bg-white hover:bg-indigo-50 sm:w-auto"
      >
      Play Again
    </button>
  </div>
)}

Now we see a nice completed state at the end of the game.

Play again button

We are almost done! For our “Play Again” button we want to reset the state all of the game. We also want to get a new list of players from the server without needing a refresh. We do it like this:

const playAgain = async () => {
  setPickedCountry("");
  setPlayersData([]);
  const response = await fetch(
    process.env.NEXT_PUBLIC_API_URL + "/api/newGame"
  );
  const data = await response.json();
  setPlayersData(data.players);
  setCurrentStep(0);
  setScore(0);
};

<button
  autoFocus
  onClick={playAgain}
  className="outline-none mt-8 w-full inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-indigo-600 bg-white hover:bg-indigo-50 sm:w-auto"
>
  Play Again
</button>

Notice we are using the environment variable we set up before via the process.env object. We are also updating our playersData by overriding our server state with our client state that we just retrieved.

We haven’t filled out our newGame route yet, but this is easy with Next.js and Netlify serverless functions . We only need to edit the file in pages/api/newGame.ts.

import { NextApiRequest, NextApiResponse } from "next"
import { top100Players } from "../../lib/players";

export default (req: NextApiRequest, res: NextApiResponse) => {
  const randomizedPlayers = top100Players.sort((a, b) => 0.5 - Math.random());
  const top5Players = randomizedPlayers.slice(0, 5);
  res.status(200).json({players: top5Players});
}

This looks much the same as our getServerSideProps because we can reuse our nice helper variables.

If we go back to the app, notice the “Play Again” button works as expected.

Improving focus states

One last thing we can do to improve our user experience is set the focus on the country input field every time the step changes. That’s just a nice touch and convenient for the user. We do this using a ref and a useEffect:

const inputRef = useRef(null);
...
useEffect(() => {
  inputRef?.current?.focus();
}, [currentStep]);

<input
  list="countries"
  type="text"
  value={pickedCountry}
  onChange={(e) => setPickedCountry(e.target.value)}
  ref={inputRef}
  className="p-2 outline-none"
  placeholder="Choose Country"
/>

Now we can navigate much easier just using the Enter key and typing a country.

Deploying to Netlify

You may be wondering how we deploy this thing. Well, using Netlify makes it so simple as it detects a Next.js application out of the box and automatically configures it.

All I did was set up a GitHub repo and connect my GitHub account to my Netlify account. From there, I simply pick a repo to deploy and use all the defaults.

The one thing to note is that you have to add the NEXT_PUBLIC_API_URL environment variable and redeploy for it to take effect.

You can find my final deployed version here.

Also note that you can just hit the “Deploy to Netlify” button on the GitHub repo.

Conclusion

Woohoo, you made it! That was a journey and I hope you learned something about React, Next.js, and Netlify along the way.

I have plans to expand this tennis trivia app to use Supabase in the near future so stay tuned!

If you have any questions/comments feel free to reach out to me on Twitter.


The post Building a Tennis Trivia App With Next.js and Netlify appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Categories: Designing, Others Tags:

Comparing Google Analytics and Plausible Numbers

October 8th, 2021 No comments

I saw this blog post the other day: 58% of Hacker News, Reddit and tech-savvy audiences block Google Analytics. That’s an enticing title to me. I’ve had Google Analytics on this site literally from the day I launched it. While we tend to see some small growth each year, I’m also aware that the ad-blocking usage (and general third-party script blocking) goes up over time. So maybe we have more growth than we can easily visualize in Google Analytics?

The level of Google Analytics blockage varies by industry, audience, the device used and the individual website. In a previous study, I’ve found that less than 10% of visitors block Google Analytics on foodie and lifestyle sites but more than 25% block it on tech-focused sites.

Marko Saric

Marko looked at three days of data on his own website when he had a post trending on both Hacker News and Reddit, and that’s where the 58% came from. Plausible analytics reported 50.9k unique visitors and Google Analytics reported 21.1k. (Google Analytics calls them “Users.”)

I had to try this myself. If we’re literally getting double the traffic Google Analytics says we are, I’d really like to know that. So for the last week, I’ve had Plausible installed on this site (they have a generous unlimited 30-day free trial).

Here’s the Plausible data:

And here’s the Google Analytics data for the same exact period:

It’ll be easier to digest in a table:

Metric Plausible Google Analytics
Unique Visitors 973k 841k
Pageviews 1.4m 1.5m
Bounce Rate 82% 82%
Visit Duration 1m 31s 1m 24s

So… the picture isn’t exactly clear. On one hand, Plausible reports 15% more unique visitors than Google Analytics. Definitely not double, but more! But then, as far as raw traffic is concerned (which actually matters more to me as a site that has ads), Plausible reports 5% less traffic. So it’s still fairly unclear to me what the real story is.

I do think Plausible is a pretty nice bit of software. Easy to install. Simple and nice charts. Very affordable.

Plausible is also third-party JavaScript

I’m sure less people block Plausible’s JavaScript, as, basically, it’s less-known and not Google. But it’s still third-party JavaScript and just as easily blockable as anything else. It’s not a fundamentally different way to measure traffic.

What is fundamentally different is something like Netlify Analytics, where the data comes from server logs that are not blockable (we’ve mentioned this before). That has its own issues, like the fact that Netlify counts bots and Google Analytics doesn’t. Maybe the best bet is something like server-side Google Analytics? That’s just way more technical debt than I’m ready for. Certainly a lot harder than slapping a script tag on the page.

Multiple sources

It felt weird to have multiple analytics tracking on the site at the same time. If I was doing things like tracking custom events, that would get even weirder. If I was going to continue to do it all client-side, I’d probably reach for something like Analytics which is designed to abstract that. But I prefer the idea of sending the data from the client once, and then if it has to go multiple places, do that server-side. That’s basically the entire premise of Segment, which is expensive, but you can self-host or probably write your own without terribly too much trouble. Just seems smart to me to keep less data going over the wire, and having it be first-party JavaScript.


The post Comparing Google Analytics and Plausible Numbers appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Categories: Designing, Others Tags: