Tech News

Ground Rules for Web Animations

Css Tricks - Mon, 08/31/2020 - 4:45am

Animations can make a site stand out. Or, they can just as easily kill the experience. When working with web animations, there are a few things that could go wrong like adding animations that serve no purpose, setting durations that are  too long or too quick, or not using right type of animation in the first place. Even if all of these things are done correctly, an animation  style may not feel good, especially if they are not in sync with other animations or in line with the overall personality of the site.

Another important thing to note is that not all digital experiences should share the exact same animations. A marketing website might need different animations than a product website or a mobile app. Although the same basic principles of motion apply for all, there’re some nuances based on content type and screen size. 

For example, say you want to make a boring form more exciting to fill out. You add some delightful animations in each step moving forward, but is that a good idea for a form you know a user needs to visit and fill often? Watching the same animations over and over could get annoying in that case.

Clearly, there are conditions and considerations that will serve animations well. In this article, we’ll discuss about adding animations into product websites. Let’s dig into that a bit and lay down some ground rules for working with them. Not so much a manifesto, but more like a baseline we can reference and sort of rally around.

First off, what’s a good situation for an animation?

When used well, an animation is almost like content — it provides context and has meaning that helps inform the user that something has happened and even what to expect next. Here are a few good situations where animation can do exactly that.

Transitioning UI blocks

This might be the most common use case for animations. When a UI block is moved from its original position, or is added or removed from the DOM, it’s a good idea to let users see that happen.

It’s easy to see the change with animation …but it’s hard to figure out what changed without it. Loading content

A loading animation is something we’ve all seen and encountered at some point in time! If not, a quick trip to CodePen shows you just how popular loading animations are. They’re ideal as placehholders for content, where users are not only given a hint at what to expect when the content loads, but confidence that something is being loaded at all.

Besides making the site feel fast, it also avoids janky content reflow, which can be super disorienting as elements render at different times.

Client side rendering is so interesting. Look at this janky loading experience. The page itself isn’t particularly slow, but it loads in very awkwardly. A whole thing front-end devs are going to have to get good at. pic.twitter.com/sMcD4nsL98

— Chris Coyier (@chriscoyier) October 30, 2018

Loading placeholders are best, of course, when you know the height of content blocks ahead of time.

Hinting

This is generally a one-time animation where the point is to give users a hint for where to look or what to do next. Some UIs are complex by nature. A little glow or ripple can help guide users through the process of completing a task or calling out a particular feature.

It doesn’t have to be all up in the user’s face. Instead, a little visual hint that informs without taking over the entire experience will do just fine.

Micro-interactions

Generally used on individual elements, micro-interactions give users instant visual feedback after performing an action. They instill confidence that a performed action has taken place and that something happened as a result — all while adding a little delight at the same time. 

These do not have to be fancy, like Twitter’s heart animation, but they totally should indicate some kind of feedback or response to the user’s action. Just check out how subtle — yet delightful — that is when a user does something as small as adding an item from one line to another:

It’s small, but that little bounce provides instant feedback to user’s action. Um, ok, so what just happened? It’s hard to tell when there’s no response. OK, so when should we avoid animations?

We’ve just seen handful of situations where animations make a lot of sense. Let’s spell out the opposite conditions where animations generally contribute very little or nothing to the user experience. In other words, they become noticeable for bad reasons and are probably best left out of the equation.

Route transitions

Yes, we usually don’t see these sorts of animations on product websites but it’s worth mentioning to understand why they don’t make sense. These transitions work better on mobile apps because of the small screen area. On desktop screens there’s much larger area to animate. To animate the whole content smoothly, you’ll require to set more duration than on mobile screen. This will simply annoy the users making them wait to see the content as they are already used to see instant content visibility on the web. And in the worst cases, route transitions can not only be distracting, but a severe accessibility concern when it comes to motion sensitivity.

On initial load of page content

You may do it in marketing websites when you want to educate users or move their focus  to a particular block. For product websites, it will be again annoying to see the same animation each time users navigates between pages.

When it’s unexpected

It’s a good idea to consider a user’s state of mind while they use a particular feature. Is visual feedback expected where the animation is being used? If not, it can confuse more than it helps.

For example, checkout this calculator app. There’s nothing new in the UI pattern when numbers are entered and calculations run. Users already know where to focus. There’s no point in making users wait before they can see results or surprise them with something that provides no additional meaning or benefit.

A snappy change without an animation is perfect in this case. The button hover and active states are more than enough. A snappy change without an animation is perfect in this case. The button hover and active states are more than enough. When you’re unsure how well it performs

It’s worth bearing in mind that not all devices, internet connections, and browsers are equal in the eyes of animation. Eric Bailey sums this up nicely in his deep-dive on the prefers-reduced-motion media query:

We also need to acknowledge that not every device that can access the web can also render animation, or render animation smoothly. When animation is used on a low-power or low quality device that “technically” supports it, the overall user experience suffers. Some people even deliberately seek this experience out as a feature.

The heading above that quote is a sage reminder: Animation is progressive enhancement. If we plan on using an animation — especially ones that threaten to dominate the experience — we’ve gotta at least consider a way to opt out of it and whether the experience still works without the animation. prefers-reduced-motion is the best place to start.

When the purpose isn’t clear

Lastly, I’d say don’t add animations wherever you’re not absolutely sure about the purpose it serves. Superfluous animation can be distracting and hurt more than it helps. This tweet from 2018 is still very true:

Web design in 2018

428 dependencies
142 seconds compile time
5 MB of JavaScript
0 clue as to basic UI interactions pic.twitter.com/1GAAQS4td8

— Thomas #BlackLivesMatter (@thomasfuchs) March 27, 2018 How long should an animation last?

The length of an animation is just as important as the type of animation being used. Wait too long, and the animation can appear to drag on. Go too fast, and the nice details of the animation can get lost (in best cases) or completely disorient the user (in worse cases).

So, how long should we set the duration of an animation? I’ll give you a classic answer: It depends.

The bigger the distance, generally the longer the duration

Animations (like the ones we looked at earlier) can be limited to a short duration. But if we’re taking about a massive transition where an object is traveling a long distance, we may feel it needs something a little longer to make sure things don’t move too fast. But avoid using duration longer than 400ms.

Check out this example. Notice how the content takes a little longer to transition because it has a greater distance to travel. But also notice that the it doesn’t have to last too long because the object that leaves fades into the object that enters, and the object that enters comes at a shorter distance rather than making it travel across the entire screen.

Goes to show that even big animations can be optimized in ways that make it feel shorter without getting lost in the mix.

Use a shorter duration when the user triggers the action

This is important and a common mistake. If the user already expects something to happen — and the focus is already clearly where it should be — then there’s no point in making the user wait seconds to complete what they already expect.

Instant reaction to what user is expecting Making user wait…

On the other hand, if the change is automatically triggered by the system, a longer duration makes sense, as it will allow the user to catch up to speed with the change taking place. Think of tooltips or modals that are not triggered by the user do not require a their immediate attention.

Less distracting with subtle entrance Too distracting with short animation duration Enter and exit animations can have different durations 

Sometimes it makes sense to keep the animation for an object that is entering view a little faster than an animation for an object that is exiting, especially when the user is expecting to see that content change.

Take the previous example of dropdown menu. When a user clicks on it, they’d want to see the menu items right away — at least, I wouldn’t have to wait to see menu items. When the user clicks, let the submenu enter quickly and then smoothly leave when it’s dismissed so that it avoids distracting the user on the way out, when it’s no longer needed.

But this does not apply for large UI blocks. On larger blocks, for most cases, a duration longer than 200ms is required. In such cases, reversing the durations and letting a block exit faster than it entered ensures it won’t block the existing page view.

Doesn’t block the page view on exit Blocks the page view on exit Animation duration across the product should be in sync with each other and with the brand’s personality

I’ve came across many products where one feature has really nice animations and another is simply too quick, slow or lacks any animation at all. 

Even worse is when animations within the same feature aren’t in sync.

Notice how the sidebar animates when it enters view, but also how it is totally out of sync with the animation that changes the width of the main content. It feels unnatural when they aren’t in harmony.

That’s where having a style guide with thoughtful animation guidelines that can be used consistently across the experience can be a huge help.

How simple is too simple? Or how complex is too complex?

The complexity of an animation ought to be based on how frequently users are expected to encounter it, among the other things we’ve looked at so far. The more often users are expected to see it, the simpler the animation should be. This should override the previous rules of duration where necessary.

For example, the below animation would work in a main menu, but using the same staggering effect in drop-down menus across the product is just too much to take in. There is indeed a point of diminishing returns in animations, just as there is in economics.

But, hey, if this sort of complex animation is used sparingly in intentional instances, then it can be incredibly delightful!

But yes, you can be creative with the animations where there’s a decision pending at the user or while processing data. This makes waiting times more engaging, like when network breaks or a wrong passcode is submitted.

Which easing function should you use?

Ease? Ease in? Ease out? Ease in and out? Some cubic bezier curve?

The right easing adheres to the laws of physics. Disney’s principles of animation is the gold standard when it comes to that.

For enter animations, use bounce effect if you want immediate attention of the user, otherwise use a smooth acceleration (and deceleration, for that matter) that is incremental rather than linear. Bouncing should reflect gravity. Brandon Gregory’s post on natural-feeling animations provides all kinds of examples that fall in line with the laws of physics.

CodePen Embed Fallback

You can refer to this Gist by Adam Argyle for defining easings in CSS.

Lights, camera, and… intentional action!

Attention to detail is what separates outstanding animations from ordinary (or even straight up broken) ones. If you’re in the process of learning web animations or currently working on a project that calls for them, I sure hope this post can serve as a set of useful ground rules to help you get the most out of your work.

Apart from the rules, I’d also mention that good animations take time and practice. Sure, a lot of the stuff I covered here is somewhat anecdotal and based on personal experience, but that’s the result of developing an eye for animations after years of working with them. Learn, try, improve, and keep learning. Otherwise, you may end up with a collection of animations that deliver poor user experiences and even hurt the accessibility of your site.

The post Ground Rules for Web Animations appeared first on CSS-Tricks.

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

a11y is web accessibility

Css Tricks - Fri, 08/28/2020 - 12:33pm

Eric Bailey eviscerates the notion that the term “a11y” isn’t accessible. It’s a hot take that I’ve had myself, embarrassingly enough.

I never see people asking why WWI is written out the way it is, either. Won’t people confuse that with the first Wonder Woman movie? Or the first season of Westworld? Or some new Weight Watchers product? I also never see people questioning technical numeronyms like P2P, S3, or W3C?

If you are seeing the term for the first time and are confused, it’s extremely easy to search for and figure out. There are heaping piles of examples of people using it for very legitimate sites, products, conferences, and more. It’s no more of a spell-checking foul as any other industry jargon and easy enough to ignore.

Plus, you can always introduce it with semantic HTML:

Like any other abbreviation, I observe the Web Content Accessibility Guideline’s (WCAG) Success Criterion 3.1.4. Like any other acronym or industry jargon, I spell out the term in full the first time it appears in my writing, then follow it up with the acronym it represents:

Accessibility (<abbr>a11y</abbr>)

It reminds me of the term serverless. The obligatory hot take on it is that servers are still in use, but the quicker you get over it, the quicker you can get to realizing it’s a powerful industry-changing idea.

Direct Link to ArticlePermalink

The post a11y is web accessibility appeared first on CSS-Tricks.

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

Here’s How I Solved a Weird Bug Using Tried and True Debugging Strategies

Css Tricks - Fri, 08/28/2020 - 4:49am

Remember the last time you dealt with a UI-related bug that left you scratching your head for hours? Maybe the issue was happening at random, or occurring under specific circumstances (device, OS, browser, user action), or was just hidden in one of the many front-end technologies that are part of the project?

I was recently reminded of how convoluted UI bugs can be. I recently fixed an interesting bug that was affecting some SVGs in Safari browsers with no obvious pattern or reason. I had searched for any similar issues to get some clue about what was going on, but I found no useful results. Despite the obstacles, I managed to fix it.

I analyzed the problem by using some useful debugging strategies that I’ll also cover in the article. After I submitted the fix, I was reminded of the advice Chris has tweeted out a while back.

Write the article you wish you found when you googled something.

— Chris Coyier (@chriscoyier) October 30, 2017

…and here we are.

Here’s the problem

I found the following bug on a project I’ve been working on. This was on a live site.

I reproduced this issue with any paint event, for example, resizing the screen.

I created a CodePen example to demonstrate the issue so you can check it out for yourself. If we open the example in Safari, the buttons might look as expected on load. But if we click on the first two larger buttons, the issue rears its ugly head. 

Whenever a browser paint event happens, the SVG in the larger buttons render incorrectly. It simply gets cut off. It might happen randomly on load. It might even happen when the screen is resized. Whatever the situation, it happens!

Ugh, why is the SVG icon getting cut off?!

Here’s how I approached the problem.

First off, let’s consider the environment

It’s always a good idea to go over the details of the project to understand the environment and conditions under which the bug is present.

  • This particular project is using React (but is not required for following this article).
  • The SVGs are imported as React components and are inlined in HTML by webpack.
  • The SVGs have been exported from a design tool and have no syntax errors.
  • The SVGs have some CSS applied to them from a stylesheet.
  • The affected SVGs are positioned inside a <button> HTML element.
  • The issue occurs only in Safari (and was noticed on version 13).
Down the rabbit hole

Let’s take a look at the issue and see if we can make some assumptions about what is going on. Bugs like this one get convoluted, and we won’t immediately know what is going on. We don’t have to be 100% correct in our first try because we’ll go step-by-step and form hypotheses that we can test to narrow down the possible causes. 

Forming a hypothesis

At first, this looks like a CSS issue. Some styles might be applied on a hover event that breaks the layout or the overflow property of the SVG graphic. It also looks like the issue is happening at random, whenever Safari renders the page (paint event when resizing the screen, hover, click, etc.).

Let’s start with the simple and most obvious route and assume that CSS is the cause of the issue. We can consider the possibility that there is a bug in the Safari browser that causes SVG to render incorrectly when some specific style applies to the SVG element, like a flex layout, for example.

By doing so, we’ve formed a hypothesis. Our next step is to set up a test that is either going to confirm or contradict the hypothesis. Each test result will produce new facts about the bug and help form further hypotheses. 

Problem simplification

We’ll use a debugging strategy called problem simplification to try and pinpoint the issue. Cornell University’s CS lecture describes this strategy as “an approach to gradually eliminate portions of the code that are not relevant to the bug.”

By assuming the issue lies within CSS, we can end up pinpointing the issue or eliminating the CSS from the equation, reducing the number of possible causes and the complexity of the problem. 

Let’s try and confirm our hypothesis. If we temporarily exclude all non-browser stylesheets, the issue should not occur. I did that in my source code by commenting out the following line of code in my project.

import 'css/app.css';

I have created a handy CodePen example to demonstrate these elements without CSS included. In React, we are importing SVG graphics as components, and they are inlined in HTML using webpack.

CodePen Embed Fallback

If we open this pen on Safari and click on the button, we are still getting the issue. It still happens when the page loads, but on CodePen we have to force it by clicking the button. We can conclude that the CSS isn’t the culprit, but we can also see that only the two out of five buttons break under this condition. Let’s keep this in mind and move on to the next hypothesis.

The same SVG elements still break with excluded stylesheets (Safari 13) Isolating the issue

Our next hypothesis states that Safari has a bug when rendering SVG inside an HTML <button> element. Since the issue has occurred on the first two buttons, we’ll isolate the first button and see what happens.

Sarah Drasner explains the importance of isolation and I highly recommend reading her article if you want to learn more about debugging tools and other approaches.

Isolation is possibly the strongest core tenets in all of debugging. Our codebases can be sprawling, with different libraries, frameworks, and they can include many contributors, even people who aren’t working on the project anymore. Isolating the problem helps us slowly whittle away non-essential parts of the problem so that we can singularly focus on a solution.

A “reduced test case” it is also often called. 

I moved this button to a separate and empty test route (blank page). I created the following CodePen to demonstrate that state. Even though we’ve concluded that the CSS is not the cause of the issue, we should keep it excluded until we’ve found out the real cause of the bug, to keep the problem simple as possible.

CodePen Embed Fallback

If we open this pen in Safari, we can see that we can no longer reproduce the issue and the SVG graphic displays as expected after clicking the button. We shouldn’t consider this change as an acceptable bug fix, but it gives a good starting point in creating a minimal reproducible example.

A minimal reproducible example

The main difference between the previous two examples is the button combination. After trying out all possible combinations, we can conclude that this issue occurs only when a paint event occurs on a larger SVG graphic that is alongside a smaller SVG graphic on the same page.

We created a minimal reproducible example that allows us to reproduce the bug without any unnecessary elements. With minimal reproducible example, we can study the issue in more detail and accurately pinpoint the part of the code causing it.

I’ve created the following CodePen to demonstrate the minimal reproducible example.

CodePen Embed Fallback

If we open this demo in Safari and click on a button, we can see the issue in action and form a hypothesis that these two SVGs somehow conflict with one another. If we overlay the second SVG graphic over the first, we can see that the size of the cropped circle on the first SVG graphic matches the exact dimensions of the smaller SVG graphic.

Edited image that compares the smaller SVG graphic to the first SVG graphic with the bug present Divide and conquer

We’ve narrowed down the issue to the combination of two SVG graphics. Now we’re going to narrow things down to the specific SVG code that’s messing things up. If we only have a basic understanding of SVG code and want to pinpoint the issue, we can use a binary tree search strategy with a divide-and-conquer approach. Cornell University’s CS lecture describes this approach:

For example, starting from a large piece of code, place a check halfway through the code. If the error doesn’t show up at that point, it means the bug occurs in the second half; otherwise, it is in the first half. 

In SVG, we can try deleting <filter> (and also <defs> since it’s empty anyway) from the first SVG. Let’s first check what <filter> does. This article by Sara Soueidan explains it best.

Just like linear gradients, masks, patterns, and other graphical effects in SVG, filters have a conveniently-named dedicated element: the <filter> element.

A <filter> element is never rendered directly; its only usage is as something that can be referenced using the filter attribute in SVG, or the url() function in CSS.

In our SVG, <filter> applies a slight inset shadow at the bottom of the SVG graphic. After we delete it from the first SVG graphic, we expect the inner shadow to be gone. If the issue persists, we can conclude that something is wrong with the rest of the SVG markup.

I’ve created the following CodePen to showcase this test.

CodePen Embed Fallback

As we can see, the issue persists anyway. The inset bottom shadow is displayed even though we’ve removed the code. Not only that, now the bug appears on every browser. We can conclude that the issue lies within the rest of the SVG code. If we delete the remaining id from <g filter="url(#filter0_ii)">, the shadow is fully removed. What is going on?

Let’s take another look at the previously mentioned definition of the <filter> property and notice the following detail:

A <filter> element is never rendered directly; its only usage is as something that can be referenced using the filter attribute in SVG.

(Emphasis mine)

So we can conclude that the filter definition from the second SVG graphic is being applied to the first SVG graphic and causing the error.

Fixing the issue

We now know that issue is related to the <filter> property. We also know that both SVGs have the filter property since they use it for the inset shadow on the circle shape. Let’s compare the code between the two SVGs and see if we can explain and fix the issue.

I’ve simplified the code for both SVG graphics so we can clearly see what is going on. The following snippet shows the code for the first SVG.

<svg width="46" height="46" viewBox="0 0 46 46"> <g filter="url(#filter0_ii)"> <!-- ... --> </g> <!-- ... --> <defs> <filter id="filter0_ii" x="0" y="0" width="46" height="46"> <!-- ... --> </filter> </defs> </svg>

And the following snippet shows the code for the second SVG graphic.

<svg width="28" height="28" viewBox="0 0 28 28"> <g filter="url(#filter0_ii)"> <!-- ... --> </g> <!-- ... --> <defs> <filter id="filter0_ii" x="0" y="0" width="28" height="28"> <!-- ... --> </filter> </defs> </svg>

We can notice that the generated SVGs use the same id property id=filter0_ii. Safari applied the filter definition it read last (which, in our case, is the second SVG markup) and caused the first SVG to become cropped to the size of the second filter (from 46px to 28px). The id property should have a unique value in DOM. By having two or more id properties on a page, browsers cannot understand which reference to apply, and the filter property redefines on each paint event, dependent on the racing condition that causes the issue to appear randomly.

Let’s try assigning unique id attribute values to each SVG graphic and see if that fixes the issue.

CodePen Embed Fallback

If we open the CodePen example in Safari and click the button, we can see that we fixed the issue by assigning a unique ID to <filter> property in each SVG graphic file. If we think about the fact that we have non-unique value for an attribute like id, it means that this issue should be present on all browsers. For some reason, other browsers (including Chrome and Firefox) seem to handle this edge-case without any bugs, although this might be just a coincidence.

Wrapping up

That was quite a ride! We started barely knowing anything about an issue that seemingly occurred at random, to fully understanding and fixing it. Debugging UI and understanding visual bugs can be difficult if the cause of the issue is unclear or convoluted. Luckily, some useful debugging strategies can help us out.

First, we simplified the problem by forming hypotheses which helped us eliminate the components that were unrelated to the issue (style, markup, dynamic events, etc.). After that, we isolated the markup and found the minimal reproducible example which allowed us to focus on a single chunk of code. Finally, we pinpointed the issue with a divide-and-conquer strategy, and fixed it.

Thank you for taking the time to read this article. Before I go, I’d like to leave you with one final debugging strategy that is also featured in Cornell University’s CS lecture

Remember to take a break, relax and clear your mind between debugging attempts.

 If too much time is spent on a bug, the programmer becomes tired and debugging may become counterproductive. Take a break, clear your mind; after some rest, try to think about the problem from a different perspective.

References

The post Here’s How I Solved a Weird Bug Using Tried and True Debugging Strategies appeared first on CSS-Tricks.

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

Copy the Browser’s Native Focus Styles

Css Tricks - Fri, 08/28/2020 - 4:49am

Remy documented this the other day. Firefox supports a Highlight keyword and both Chrome and Safari support a -webkit-focus-ring-color keyword. So if you, for example, have removed focus from something and want to put it back in the same style as the browser default, or want to apply a focus style to an element when it isn’t directly in focus itself, this can be useful.

For example:

button:focus + span { outline: 5px auto Highlight; outline: 5px auto -webkit-focus-ring-color; }

Looks good to me. It’s especially helpful with the sorta weird new Chrome double-outline style that would be slightly tricky to replicate otherwise.

Chrome 84 Safari 13.1 Firefox 80

The post Copy the Browser’s Native Focus Styles appeared first on CSS-Tricks.

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

Deeper DX

Css Tricks - Thu, 08/27/2020 - 11:09am

Shawn Wang thinks there are deeper, perhaps more uncomfortable, places to go with developer experience (DX) beyond the surface-level stuff that we recently covered. Sure, sure, documentation, CLIs, good demos. But there are much harder questions to answer that are part of the real DX. Shawn lists eight really good ones. I’ll share this one:

No product launches feature complete. Nobody expects you to. The true test is whether you address it up front or hide it like a dirty secret. As developers explore your offering, they will find things they want, that you don’t have, and will tell you about it. How long do you make developers dig to find known holes in your product? Do developers have confidence you will ship or reject these features promptly, or are they for a “v2” that will never come?

Direct Link to ArticlePermalink

The post Deeper DX appeared first on CSS-Tricks.

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

A Bit on CI/CD

Css Tricks - Wed, 08/26/2020 - 1:42pm

I’d say “website” fits better than “mobile app” but I like this framing from Max Lynch:

Every production mobile app ultimately has a set of recurring tasks around integration, testing, deployment, and long term maintenance. These tasks often must be automated across a team of many developers and app projects. Building a process for these tasks can be incredibly time consuming and require specialized infrastructure experience, but is critical for the success of any serious app project.

They are talking about “Continuous Integration and Continuous Deployment,” or CI/CD.

Everybody is trying to get you on their CI/CD tooling, and it’s obvious why: it’s a form of lock-in. This stuff is hard, so if they can help make it easier, that’s great, but they tend to do it in their own special way, which means you can’t just up and leave without causing a bunch of work for yourself. I ain’t throwing shade, it’s just how it is.

So much CI/CD stuff crosses my attention:

I’m probably missing at least 20 companies here. Like I say, everybody wants you on their system. They want you storing your secrets there. They want you configuring your permissions there.

The post A Bit on CI/CD appeared first on CSS-Tricks.

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

Why you should hire a front-end developer

Css Tricks - Wed, 08/26/2020 - 10:01am

Matt Hobbs says you should hire a front-end developer because…

  • “A front-end developer is the best person to champion accessibility best practices in product teams.”
  • “80-90% of the end-user response time is spent on the front end.”
  • “A front-end developer takes pressure off interaction designers.”
  • “If you do not have a front-end developer there is a high risk that the good work the rest of the team does will not be presented to users in the best way.”

Direct Link to ArticlePermalink

The post Why you should hire a front-end developer appeared first on CSS-Tricks.

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

What’s New In DevTools (Chrome 86)

Css Tricks - Wed, 08/26/2020 - 4:45am

It wasn’t that long ago that Umar Hansa published a look at the most interesting new features in Chrome DevTools released in 2020. In fact, it was just earlier this month!

But in that short amount of time, Chrome has a few new tricks up its sleeve. One of the features Umar covered was the ability to emulate certain browsing conditions including, among many, vision deficiencies like blurred vision.

Chrome 86 introduces new emulators!

  • Emulate missing local fonts (great for testing when a user’s device does not have an installed font)
  • Emulate prefers-reduced-data (to complement Chrome support for this new feature!)
  • Emulate inactive users (yay, no more multiple browser windows with different user accounts!)

Direct Link to ArticlePermalink

The post What’s New In DevTools (Chrome 86) appeared first on CSS-Tricks.

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

Linkbait 46

QuirksBlog - Wed, 08/26/2020 - 3:45am

Baiting you into clicking.

  • This presentation by Stephanie Rieger makes the case for regulation of the web in order to get rid of hate speech and fake news. Worth thinking about.
    While reading through it I had a thought: what if we’d require payments for social media memberships? It’s certain to be quite complicated, so before we do anything we should discuss if it would help at all.
  • Rachel Andrew explains an interesting payment experiment that I missed: Igalia, the company that implements rather more browser features than most people know, started an Open Prioritization drive where people can pay to get CSS features implemented. Once a feature is fully funded it’s pushed into Igalia’s work queue.
    Simple idea, might work. More of this, please!
  • An ambitious project: Web History, chapter 1: Birth, chapter 2: Browsers, and chapter 3: The Website. The first chapter is less interesting to me, because I already know most of the story. 2 and 3 already contain a few items I didn’t know about. I expect this series to become much more interesting to me once we get to later chapters, the ones I can remember.
  • Nice project: Modern CSS solutions for old CSS problems. It revisits a few golden oldies such as always keeping the footer at the page bottom no matter the content size. How would we solve these problems with modern CSS?
    Even better, Stephanie Eckles is running Style Stage, a showcase site where people can provide their own CSS for a given HTML page. (Sounds familiar to an old-timer?) Let's hope it’s a massive success; we need it.
  • Interesting details about CSS gap: it’s supported on flex and grid, except in Safari, where it only works on grid. And how do you detect support? Safari does support gap, so @supports (gap: 1em) will return true but hide the fact that it’s not supported on flex. (See also this discussion.)
    Ahmad Shaheed offers a JavaScript workaround that works, but it involves creating and measuring a test element, which is something that could kill your performance if overused.
    Anyway, interesting read if you’re into the problems of @supports, as I am.
  • An in-depth look at the load and DOMContentLoaded events and their differences. Contains quite a few details I didn’t know about, such as the rules for deferred and async script tags. Worth a look if you’re struggling with initialisation and up-front load times.
  • Native JS formatting options for numbers: a very useful overview, with simple pictures to begin with. Turns out there are quite a few more internationalisation options for numbers in JavaScript than I was aware of.
  • An serious discussion of the performance consequences of React and Preact when creating a simple site. The message is familiar: React, and to a lesser extent Preact, degrade your website’s performance. That doesn’t mean you shouldn’t use them, but it does mean you should think before using them, especially in cases of simple websites.
    The article also calls for keeping sites accessible to users of old or low-specced devices. I agree, but the chance of this happening is slight.
  • Speaking of old or low-specced devices, here’s a look at their cost, not in money but in days worked.
    Device cost is but one factor, and connection cost is the other, but it’s an up-front one that people may struggle with. Governments can help here, especially in times of Corona.
  • An interesting story about Yugoslavian microcomputers in the eighties. Turned out there was quite a scene, and one radio DJ even broadcast programs! Programs were stored on cassette tapes (C64 aficionados will know how that goes), and listeners to this show could just tape whatever program the radio station broadcast and run it on their own computers. Never heard of this trick before.
    Update: A reader told me this broadcasting of computer programs also took place in the Netherlands. So it's not as unique as I thought.
  • I always love this sort of project: a to-scale map of the solar system where the Moon is one pixel. Takes a LONG time to scroll through. We are unable to truly understand these huge amounts of space.
  • Have a tip for the next Linkbait? Or a comment on this one? Let me know (or here or here).

Doom Damage Flash on Scroll

Css Tricks - Tue, 08/25/2020 - 2:07pm

The video game Doom famously would flash the screen red when you were hit. Chris Johnson not only took that idea, but incorporated a bunch of the UI from Doom into this tounge-in-cheek JavaScript library called Doom Scroller. Get it? Like, doom scrolling, but like, Doom scrolling. It’s funny, trust me.

I extracted bits from Chris’ cool project to focus on the damage animation itself. The red flash is done in HTML and CSS. First, we create a full screen overlay:

#doom-damage { background-color: red; position: fixed; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; pointer-events: none; }

Note that it’s not display: none. It’s much harder to animate that as we have to wait until the animation is completed to apply it. That’s because display isn’t animatable. It’s doable, just annoying.

To flash it, we’ll apply a class that does it, but only temporarily.

const damage = document.getElementById("doom-damage"); function doomTakeDamage() { damage.classList.add("do-damage"); setTimeout(function () { damage.classList.remove("do-damage"); }, 400); }

When that class activates, we’ll immediately turn the screen red (really giving it that shock appeal) and then fade the red away:

.do-damage { background-color: red; animation: 0.4s doom-damage forwards; } @keyframes doom-damage { 0% { opacity: 1; } 100% { opacity: 0; } }

The next bit calls the function that does the damage flash. Essentially it tracks the current scroll position, and if it’s past the nextDamagePosition, it will red flash and reset the next nextDamagePostition one full screen height length away.

If you want to see all that, I’ve abstracted it into this Pen:

CodePen Embed Fallback

The post Doom Damage Flash on Scroll appeared first on CSS-Tricks.

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

Comparing Data in Google and Netlify Analytics

Css Tricks - Tue, 08/25/2020 - 11:16am

Jim Nielsen:

the datasets weren’t even close for me.

Google Analytics works by putting a client-side bit of JavaScript on your site. Netlify Analytics works by parsing server logs server-side. They are not exactly apples to apples, feature-wise. Google Analytics is, I think it’s fair to say, far more robust. You can do things like track custom events which might be very important analytics data to a site. But they both have the basics. They both want to tell you how many pageviews your homepage got, for instance.

There are two huge things that affect these numbers:

  • Client-side JavaScript is blockable and tons of people use content blockers, particularly for third-party scripts from Google. Server-side logs are not blockable.
  • Netlify doesn’t filter things out of that log, meaning bots are counted in addition to regular people visiting.

So I’d say: Netlify probably has more accurate numbers, but a bit inflated from the bots.

Also worth noting, you can do server-side Google Analytics. I’ve never seen anyone actually do it but it seems like a good idea.

One bit of advice from Jim:

Never assume too much from a single set of data. In other words, don’t draw all your data-driven insights from one basket.

Direct Link to ArticlePermalink

The post Comparing Data in Google and Netlify Analytics appeared first on CSS-Tricks.

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

Where Does Logic Go on Jamstack Sites?

Css Tricks - Mon, 08/24/2020 - 4:36am

Here’s something I had to get my head wrapped around when I started building Jamstack sites. There are these different stages your site goes through where you can put logic.

Let’s look at a special example so you can see what I mean. Say you’re making a website for a music venue. The most important part of the site is a list of events, some in the past and some upcoming. You want to make sure to label them as such or design that to be very clear. That is date-based logic. How do you do that? Where does that logic live?

There are at least four places to consider when it comes to Jamstack.

Option 1: Write it into the HTML ourselves

Literally sit down and write an HTML file that represents all of the events. We’d look at the date of the event, decide whether it’s in the past or the future, and write different content for either case. Commit and deploy that file.

<h1>Upcoming Event: Bill's Banjo Night</h1> <h1>Past Event: 70s Classics with Jill</h1>

This would totally work! But the downside is that weu’d have to update that HTML file all the time — once Bill’s Banjo Night is over, we have to open your code editor, change “Upcoming” to “Past” and re-upload the file.

Option 2: Write structured data and do logic at build time

Instead of writing all the HTML by hand, we create a Markdown file to represent each event. Important information like the date and title is in there as structured data. That’s just one option. The point is we have access to this data directly. It could be a headless CMS or something like that as well.

Then we set up a static site generator, like Eleventy, that reads all the Markdown files (or pulls the information down from your CMS) and builds them into HTML files. The neat thing is thatwe can run any logic we want during the build process. Do fancy math, hit APIs, run a spell-check… the sky is the limit.

For our music venue site, we might represent events as Markdown files like this:

--- title: Bill's Banjo Night date: 2020-09-02 --- The event description goes here!

Then, we run a little bit of logic during the build process by writing a template like this:

{% if event.date > now %}   <h1>Upcoming Event: {{event.title}}</h1> {% else %}   <h1>Past Event: {{event.title}}</h1> {% endif %}

Now, each time the build process runs, it looks at the date of the event, decides if it’s in the past or the future and produces different HTML based on that information. No more changing HTML by hand!

The problem with this approach is that the date comparison only happens one time, during the build process. The now variable in the example above is going to refer to the date and time the build happens to run. And once we’ve uploaded the HTML files that build produced, those won’t change until we run the build again. This means that once an event at our music venue is over, we’d have to re-run the build to make sure the website reflects that.

Now, we could automate the rebuild so it happens once a day, or heck, even once an hour. That’s literally what the CSS-Tricks conferences site does via Zapier.

The conferences site is deployed daily using a Zapier automation that triggers a Netlify deploy,, ensuring information is current.

But this could rack up build minutes if you’re using a service like Netlify, and there might still be edge cases where someone gets an outdated version of the site.

Option 3: Do logic at the edge

Edge workers are a way of running code at the CDN level whenever a request comes in. They’re not widely available at the time of this writing but, once they are, we could write our date comparison like this:

// THIS DOES NOT WORK import eventsList from "./eventsList.json" function onRequest(request) {   const now = new Date();   eventList.forEach(event => {     if (event.date > now) {       event.upcoming = true;     }   })   const props = {     events: events,   }   request.respondWith(200, render(props), {}) }

The render() function would take our processed list of events and turn it into HTML, perhaps by injecting it into a pre-rendered template. The big promise of edge workers is that they’re extremely fast, so we could run this logic server-side while still enjoying the performance benefits of a CDN.

And because the edge worker runs every time someone requests the website, we can be sure that they’re going to get an up-to-date version of it.

Option 4: Do logic at run time

Finally, we could pass our structured data to the front end directly, for example, in the form of data attributes. Then we write JavaScript that’s going to do whatever logic we need on the user’s device and manipulates the DOM on the fly.

For our music venue site, we might write a template like this:

<h1 data-date="{{event.date}}">{{event.title}}</h1>

Then, we do our date comparison in JavaScript after the page is loaded:

function processEvents(){   const now = new Date()   events.forEach(event => {     const eventDate = new Date(event.getAttribute('data-date'))     if (eventDate > now){         event.classList.add('upcoming')     } else {         event.classList.add('past')     }   }) }

The now variable reflects the time on the user’s device, so we can be pretty sure the list of events will be up-to-date. Because we’re running this code on the user’s device, we could even get fancy and do things like adjust the way the date is displayed based on the user’s language or timezone.

And unlike the previous points in the lifecycle, run time lasts as long as the user has our website open. So, if we wanted to, we could run processEvents() every few seconds and our list would stay perfectly up-to-date without having to refresh the page. This would probably be unnecessary for our music venue’s website, but if we wanted to display the events on a billboard outside the building, it might just come in handy.

Where will you put the logic?

Although one of the core concepts of Jamstack is that we do as much work as we can at build time and serve static HTML, we still get to decide where to put logic.

Where will you put it?

It really depends on what you’re trying to do. Parts of your site that hardly ever change are totally fine to complete at edit time. When you find yourself changing a piece of information again and again, it’s probably a good time to move that into a CMS and pull it in at build time. Features that are time-sensitive (like the event examples we used here), or that rely on information about the user, probably need to happen further down the lifecycle at the edge or even at runtime.

The post Where Does Logic Go on Jamstack Sites? appeared first on CSS-Tricks.

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

This vs. That

Css Tricks - Mon, 08/24/2020 - 4:36am

Here’s a nice site from Phuoc Nguyen, who I’ve noted before has quite a knack for clever sites. This vs. That pits different related concepts against each other as a theme for an article. For example, CSS has display: none;, opacity: 0;, and visibility: hidden; and they all, on the surface “hide” something, but they are all markedly different in ways that are important to understand. That’s one of the articles. The content is open source as well, if you feel like adding anything.

This reminds me of this Pen from Adam Thompson:

CodePen Embed Fallback

All that Pen is doing is setting the colors of some pill boxes, but it does it in literally seven different ways — in this case, none of them are “better” than another:

  1. Swap a class
  2. Swap a class, colors defined in Sass @mixin
  3. Swap a class, class swaps value of a custom property
  4. Swap the value of a custom property
  5. Swaps the value of a custom property, colors stored in JavaScript only
  6. Set inline styles
  7. Manipulate the CSSOM
  8. Set a non-standard color attribute

They all ultimately do the same thing. And there could be many more: change class on a higher-up parent. Use data-* attributes. Use some kind of hue-shifting filter. Use color math in JavaScript to manipulate hues. Use the checkbox hack to change styling. Surely there are even dozens more.

Direct Link to ArticlePermalink

The post This vs. That appeared first on CSS-Tricks.

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

Offering Options for mailto: and tel: Links

Css Tricks - Fri, 08/21/2020 - 11:33am

I generally like mailto: links. But I feel like I can smell a mailto: link without even inspecting or clicking it, like some kind of incredibly useless superpower. I know that if I’ve got my default mail client set, clicking that link will do what I want it to do, and if I want, I can right-click and the browser will give me a “Copy email address” option to grab it cleanly.

That’s cool and all, but Adam Silver and Amy Hupe recently enumerated the problems with how these links behave:

Firstly, mailto links make it hard to copy the address, for example if you want to share the email address with someone else.

Secondly, some users use more than one mail app, and the link just uses whichever has been setup as the default, without giving them the option to use the other.

And finally, many users don’t have an email application set up, which means the link can take them to a dead end or down a rabbit hole.

Their UI experimentation ended up using a mailto: link, but putting the entire email address as the link which makes it especially obvious what the link does, while also offering a Copy button for a little UX bonus.

tel: links are weirder in the sense that a good many devices looking at them don’t have any phone-calling functionality. If they do, it’s a lot like email links in that multiple apps could do that work (e.g. WhatsApp, FaceTime, or the default phone app).

The hard part of the UX of all this is offering users choice on what they want these special link types to do. That’s what mailgo is attempting to solve. It’s a little JavaScript library that offers UI when you click them.

Live demo:

CodePen Embed Fallback

I kinda like it. I wouldn’t mind at all if that popped up when I clicked a link like this, especially since it has that “open default” option if I want that anyway. Seems to check all the boxes for the problems these types of special links can have.

The post Offering Options for mailto: and tel: Links appeared first on CSS-Tricks.

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

A CSS-only, animated, wrapping underline

Css Tricks - Fri, 08/21/2020 - 11:33am

Nicky Meuleman, inspired by Cassie Evans, details how they built the anchor link hover on their sites. When a link is hovered, another color underline kinda slides in with a gap between the two. Typical text-decoration doesn’t help here, so multiple backgrounds are used instead, and fortunately, it works with text that breaks across multiple lines as well.

CodePen Embed Fallback

Direct Link to ArticlePermalink

The post A CSS-only, animated, wrapping underline appeared first on CSS-Tricks.

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

Leading-Trim: The Future of Digital Typesetting

Css Tricks - Fri, 08/21/2020 - 4:12am

leading-trim is a suggested new CSS property that lets us remove the extra spacing in every font so that we can more predictably style text. Ethan Wang has written about it — including how Microsoft has advocated for it — and that it’s now part of the Inline Layout Module Level 3 spec.

You’d use it like this:

h1 { leading-trim: both; text-edge: cap alphabetic; }

This is telling the browser to look at the font file, dig into the OpenType metrics, and effectively do what Ethan demonstrates in this gif:

Why do we want to do this? Well, it would let us space text inside a button properly without any strange hacks and we’d be able to set predictable spacing values between different typefaces too. I’m pretty excited about this spec and the CSS property because it gives us yet one more tool to control the use of typography on the web — like taming line height.

Direct Link to ArticlePermalink

The post Leading-Trim: The Future of Digital Typesetting appeared first on CSS-Tricks.

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

Optimize Images with a GitHub Action

Css Tricks - Thu, 08/20/2020 - 11:56am

I was playing with GitHub Actions the other day. Such a nice tool! Short story: you can have it run code for you, like run your build processes, tests, and deployments. But it’s just configuration files that can run whatever you need. There is a whole marketplace of Actions wanting to do work for you.

What I wanted to do was run code to do image optimization. That way I never have to think about it. Any image in the repo has been optimized.

There is an action for this already, Calibre’s image-actions, which we’ll leverage here. You’ll also need to ensure Actions is enabled for the repo. I know in my main organization we only flip on Actions on a per-repo basis, which is one of the options.

Then you make a file at ./github/workflows/optimize-images.yml. That’s where you can configure this action. All your actions can have separate files, if you want them to. I made this a separate file because (1) it only works with “pushes to pull requests,” so if you have other actions that run on different triggers, they won’t mix nicely, and (2) That’s what is in their docs and looks like the suggested usage.

name: Optimize images on: pull_request jobs: build: name: calibreapp/image-actions runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@master - name: Compress Images uses: calibreapp/image-actions@master with: githubToken: ${{ secrets.GITHUB_TOKEN }}

Now if you make a pull request, you’ll see it run:

That successful run then leaves a comment on the pull request saying what it was able to optimize:

It will literally re-commit those files back to the pull request as well, so if you’re going to stay on the pull request and keep working, you’ll need to push again before you can push to get the optimized images.

I can look at that automatic commit and see the difference:

The commit preview in Git Tower.

How I can merge the PR knowing all is well:

Pretty cool. Is optimizing your images locally particularly hard? No. Is never having to think about it again better? Yeah. You’re taking on a smidge of technical debt here, but reducing it elsewhere, which is a very fair trade, at least in my book.

The post Optimize Images with a GitHub Action appeared first on CSS-Tricks.

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

To grid or not to grid

Css Tricks - Thu, 08/20/2020 - 11:54am

Sarah Higley does accessibility work and finds that “tables and grids are over-represented in accessibility bugs.”

The drum has been banged a million times: don’t use a <table> for layout. But what goes around comes around. What’s the the #1 item in a list of “some of the ways tables and grids can go wrong”?

Using a grid when a table is needed, or vice versa

The day has come. CSS grid has dug its way into usage so deeply that developers are using it by default instead of using a classic <table>. And we don’t even have flying cars yet!

Sarah shows clear examples of both techniques and how the same information can be presented in different ways both visually and semantically. For example, a list of upcoming concerts can be displayed as a <table>, and that might be fine if you can imagine the purpose of the table being used for sorting or comparing, but it can also be presented as a grid, which has other advantages, like headers that are easier to skim.

Direct Link to ArticlePermalink

The post To grid or not to grid appeared first on CSS-Tricks.

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

Make the Letter Bigger

Typography - Thu, 08/20/2020 - 7:25am

A brief history of the drop-cap: Decorated or illuminated initials were an important part of medieval manuscripts for a thousand years. From luxurious gold and silver letters to plain drop capitals, they functioned to illustrate, commentate, and adorn the text. Learn their history and purpose, why they eventually went out of fashion, and what replaced them.

The post Make the Letter Bigger appeared first on I Love Typography.

Let’s Make Generative Art We Can Export to SVG and PNG

Css Tricks - Wed, 08/19/2020 - 9:48am

Let’s say you’re a designer. Cool. You’ve been hired to do some design work for a conference. All kinds of stuff. Website. Printed schedules. Big posters for the rooms. Preroll slides. You name it.

So you come up with an aesthetic for it all — a design vibe that ties it all together and makes it feel cohesive. Yet each usage will be unique and different. Cool, let’s go from there.

You’re mucking around in your design software, and the aesthetic you come up with is these overlapping rectangles in a randomized pattern with a particular limited color palette that you think can work for all the materials.

Hey, sure. That’s a fun background pattern. You can lay white boxes on top of it to set type or whatever, this is just the general background aesthetic that you can use broadly.

But it’s not very random while it’s in design software, is it? I suppose you could figure out how to script the software. But we’re web people so let’s get webby with it. Let’s lean on JavaScript and SVG to start.

We could define our color palette programmatically like:

const colorPalette = ["#9B2E69", "#D93750", "#E2724F", "#F3DC7B", "#4E9397"];

Then write a function that just makes a bunch of random rectangles based on a minimum and maximum value you give it:

const rand = (max) => { return Math.floor(Math.random() * max); }; const makeRects = (maxX, maxY) => { let rects = ""; for (let i = 0; i < 100; i++) { rects += ` <rect x="${rand(maxX + 50) - 50}" y="${rand(maxY + 50) - 50}" width="${rand(200) + 20}" height="${rand(200) + 20}" opacity="0.8${rand(10)}" fill="${colorPalette[rand(5)]}" /> `; } return rects; };

You could call that function and slap all those rectangles in an <svg> and get some nice generative artwork.

Now your work is easy! To make new ones, you run the code over and over and the you get nice SVG to use for whatever you need.

Let’s say your client is asking you for some of this artwork to use as backgrounds on other things they are working on too. They need a background with different dimensions! At a different aspect ratio! They need it right now!

The fact that we’re doing this in the browser is awfully helpful here. The browser window can be resized easily. Wow, I know. So let’s size the parent SVG to the entire viewport. This is the SVG that calls that function to make all the random rectangles here:

const makeSVG = () => { const w = document.body.offsetWidth; const h = document.body.offsetHeight; const svg = `<svg width="${w}" height="${h}"> ${makeRects(w, h)} </svg>`; return svg; };

So, if we’re doing this in the browser, we’ll get a wide and squat SVG result if the browser is super wide and squat:

But how do we get that out of the browser and into an actual SVG file? Well, there are probably native platform ways to do it, but I just Google’d my way out of it and found a snippet of code that did the trick. I take the SVG as a string, chuck it in a data URL as the href on a link, and fake-click that link. I do that on the click of a button.

function download(filename, text) { var pom = document.createElement("a"); pom.setAttribute( "href", "data:text/plain;charset=utf-8," + encodeURIComponent(text) ); pom.setAttribute("download", filename); if (document.createEvent) { var event = document.createEvent("MouseEvents"); event.initEvent("click", true, true); pom.dispatchEvent(event); } else { pom.click(); } } const downloadSvgButton = document.querySelector("#download-svg-button"); downloadSvgButton.addEventListener("click", () => { download("art.svg", window.globalSVGStore); });

But I need is as a PNG!

…cries your client. Fair enough. Not everyone has software that can view and deal with SVG. You could just take a screenshot of the page. And, honestly, that might be a good way to go. I have a high pixel density display and those screenshots turn out great.

But now that we’ve built a downloader machine for the SVG, we might as well make it work for PNG too. This time my Googling led to FileSaver.js. If I have a <canvas>, I can toBlob that thing and save it to a file. And I can turn my <svg> into a <canvas> via canvg.

So, when we call our function to make the SVG, we’ll just paint it to a canvas, which will automatically be the same size as the SVG, which we’ve sized to cover the viewport.

const setup = () => { const v = canvg.Canvg.fromString(ctx, makeSVG()); v.start(); };

We can call that setup function whenever, so might as well make a button for it, too, and call it when the browser window resizes. Here it is in action:

And here’s the final thing:

CodePen Embed Fallback

It could be a lot smarter. It could decide how many rectangles to draw based on the viewport volume, for example. I just think it’s very neat to essentially build an art-generating machine for making design assets, particularly to solve real-world client problems.

This idea was totally taken from a peek I had at a tool some actual designers built like this. Theirs was way cooler and had even more options, and I know who they built it for was very happy with it because that’s who showed it to me. I reached out to that designer but they were too busy to take on a writing gig like this.

The post Let’s Make Generative Art We Can Export to SVG and PNG appeared first on CSS-Tricks.

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

Syndicate content
©2003 - Present Akamai Design & Development.