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.
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.
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.
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.
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.
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.
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).
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.
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.
Having said that, the general workflow is always the same and includes the following steps:
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.
Create a new local branch in your forked repository. (Reminder: pull requests are based on branches, not on commits!)
Make some changes in your local branch and commit them.
Push the changes to your own remote repository.
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.
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.
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).
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:
Non-visible fields are populated (this is not the same as fields with a hidden type);
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:
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.”
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:
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.
“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.
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!
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.
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.
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.
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.
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.
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:
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.
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.
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:
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.
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 twocategories 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.
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.
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.
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:
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..
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:
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.
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:
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.
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:
{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:
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:
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.
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:
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.
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 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.
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.