Tag: from

Lessons Learned from Sixty Days of Re-Animating Zombies with Hand-Coded CSS

Caution: Terrible sense of humor ahead. We’ll talk about practical stuff, but the examples pretty much all involve zombies and silly jokes. You have been warned.

I’ll be linking to individual Pens as I discuss the lessons I learned, but if you’d like to get a sense of the entire project, check out 60 days of Animation on Undead Institute. I started this project to end on August 1st, 2020, coinciding with the publication of a book I wrote featuring CSS animation, humor, and zombies — because, obviously, zombies will destroy the world if you don’t brandish your web skills and stop the apocalypse. Nothing puts the hurt on the horde like a HTML element on the move!

I had a few rules for myself throughout the project. 

  1. I would hand-code all CSS. (I’m a masochist.)
  2. The user would initiate all of the animation. (I hate coming upon an animation that’s already halfway through.) 
  3. I would use JavaScript as little as possible and never for animation. (I only ended up using JavaScript once, and that was to start audio with the final animation. I have nothing against JavaScript, it’s just not what I wanted to do here.)

Lesson 1: Eighty days is a long time.

Uh, doesn’t the title say “sixty” days? Yes, but my original goal was to do eighty days and as day one approached with less than twenty animations prepared and a three day average for each production, I freaked out and switched to sixty days. That gave me both twenty more days till the beginning date and twenty fewer pieces to do.

Lesson 1A: Sixty days is still a long time.

That’s a lot of animation to do with a limited amount of time, ideas, and even more limited artistic skills. And while I thought of dropping to thirty days, I’m glad I didn’t. Sixty days stretched me and forced me to go deeper into how CSS animation — and by extension, CSS itself — works. I’m also proudest of many of the later pieces I did as my skills increased, and I had to be more innovative and think harder about how to make things interesting. Once you’ve used all the easy options, the actual work and best results begin. (And yes, it ended up being sixty-two days because I started on June 1 and wanted to do a final animation on August 1. Starting June 3 just felt icky and wrong.)

So, the real Lesson 1: stretch yourself.

Lesson 2: Interactive animations are hard, and even harder to make responsive. 

If you want something to fly across the screen and connect with another element or appear to start another element’s move, you must use either all standard, inflexible units or all flexible units. 

Three variables determine when and where an animated element will be during any animation: duration, velocity, and distance. The duration of the animation is set in the animation property and cannot be changed in relation to screen size. The animation timing function determines the velocity; screen size can’t change that either. Thus, if the distance varies with the screen size, the timing will be off everywhere except a specific screen width and height. 

Look at Tank!. Run the animation at wide and narrow screen sizes. While I got the timing close, if you compare the two, you’ll see that the tank is in a different place relative to the zombies when the last zombies fall.

Showing the same brown take, side by side, where the tank on the left is further along than the tank on the right.

To avoid these timing issues, you can use fixed units and a large number, like 2000 or 5000 pixels or more, so that the animation will cover the width (or height) of the screen for all but the largest monitors.  

Lesson 3: If you want a responsive animation, put everything in (one of the) viewport units. 

Going halfsies on unit proportions (e.g. setting width and height in pixels, but location and movement with viewport units) will lead to unpredictable results. Don’t use both vw and vh either but one or the other; whichever will be the dominant orientation. Mixing vh and vw units will make your animation go “wonky” which I believe is the technical term. 

Take Superbly Zomborrific, for instance. It mixes pixel, vw, and vh units. The premise is that the Super Zombie is flying upward as the “camera” follows. Super Zombie smashes into a ledge and falls as the camera continues, but you wouldn’t understand that if your screen was sufficiently tall.

Two animation frames, side by side where the left shows the flying green zombie hitting a building ceiling and the right shows the zombie leaving the frame after impact.

That also means that if you need something to come in from the top — like I did in Nobody Here But Us Humans —you must set the vw height high enough to ensure that the ninja zombie isn’t visible at most aspect ratios.

Lesson 3A: Use pixel units for movements within an SVG element. 

All that said, transforming elements within an SVG element should not use viewport units. SVG tags are their own proportional universe. The SVG “pixel” will stay proportional within the SVG element to all the other SVG element children while viewport units will not. So transform with pixel units within an SVG element, but use viewport units everywhere else.

Lesson 4: SVGs scale horribly at runtime.

For animations, like Oops…, I made the SVG image of the zombie scale up to five times his size, but that makes the edges fuzzy. [Shakes fist at “scalable” vector graphics.]

/* Original code resulting in fuzzy edges */ .zombie {   transform: scale(1);   width: 15vw; }  .toggle-checkbox:checked ~ .zombie {   animation: 5s ease-in-out 0s reverseshrinkydink forwards; }  @keyframes reverseshrinkydink {   0% {     transform: scale(1);   }   100% {     transform: scale(5);   } }

I learned to set their dimensions to the final dimensions that would be in effect at the end of the animation, then use a scale transform to shrink them down to the size for the start of the animation. 

/* Revised code */ .zombie {   transform: scale(0.2);   width: 75vw; }  .toggle-checkbox:checked ~ .zombie {   animation: 5s ease-in-out 0s reverseshrinkydink forwards; }  @keyframes reverseshrinkydink {   0% {     transform: scale(0.2);   }   100% {     transform: scale(1);   } }

In short, the revised code moves from a scaled-down version of the image up to the full width and height. The browser always renders at 1, making the edges crisp and clean at a scale of 1. So instead of scaling from 1 to 5, I scaled from 0.2 to 1.

The same animation frame of a scientist holding a coffee mug standing to the left of a growing zombie where the frame on the left shows the zombie with blurry edges and the frame on the right is clear.

Lesson 5: The axis Isn’t a universal truth. 

An element’s axes stay in sync with the element, not the page. A 90-degree rotation before a translateX will change the direction of the translateX from horizontal to vertical. In Nobody Here But Us Humans… 2, I flipped the zombies using a 180-degree rotation. But positive Y values move the ninjas towards the top, and negative ones move them towards the bottom (the opposite of normal). Beware of how a rotation may affect transforms further down the line.

Showing the main character facing us in the foreground with 7 ninja characters hanging upside down from the ceiling against a light pink background.

Lesson 6. Separate complex animations into concentric elements to make easier adjustments.

When creating a complex animation that moves in multiple directions, adding wrapper divs, or rather parent elements, and animating each one individually will cut down on conflicting transforms, and prevent you from becoming a weepy mess.

For instance, in Space Cadet, I had three different transforms going on. The first is the zomb-o-naut’s moving in an up and down motion. The second is a movement across the screen. The third is a rotation. Rather than trying to do everything in a single transform, I added two wrapping elements and did one animation on each element (I also saved my hair… at least some of it.) This helped avoid the axis issues discussed in the last lesson because I performed the rotation on the innermost element, leaving its parent’s and grandparent’s axes in place.

Lesson 7: SVG and CSS transforms are the same. 

Some paths and groups and other SVG elements will already have transforms defined on them. It could be from an optimization algorithm, or perhaps it’s just how the illustration software generates the code. If a path, group, or whatever element in an SVG already has an SVG transform on it, removing that transform will reset the element, often to a bizarre location or size compared to the rest of the drawing. 

Since SVG and CSS transforms are the same, any CSS transform you do replaces the SVG transform, meaning your CSS transform will start from that bizarre location or size rather than the location or size that is set in the the SVG.

You can copy the transform from the SVG element to your CSS and set it as the starting position in CSS (updating it to the CSS syntax first, of course). You can then modify it in your CSS animation.

For instance, in Uhhh, Yeah…, my tribute to Office Space, Undead Lumbergh’s right upper arm (the #arm2 element) had a transform on it in the original SVG code.

<path id="arm2" fill="#91c1a3" fill-rule="nonzero" d="M0 171h9v9H0z" transform="translate(0 -343) scale(4 3.55)"/>
A side by side comparison of a zombie dressed in a blue button-up shirt and black suspenders while holding a coffee cup. On the left, the arm holding the coffee mugs the the correct position but the right shows the arm detached from the body.

Moving that transform to CSS like this:

<path id="arm2" fill="#91c1a3" fill-rule="nonzero" d="M0 171h9v9H0z"/>
#arm2 {   transform: translate(0, -343px) scale(4, 3.55); }

…I could then create an animation that doesn’t accidentally reset the location and scale:

.toggle-checkbox:checked ~ .z #arm2 {    animation: 6s ease-in-out 0.15s arm2move forwards; }  @keyframes arm2move {   0%, 100% {     transform: translate(0, -343px) scale(4, 3.55);   }   40%, 60% {     transform: translate(0, -403px) scale(4, 3.55);   }   50% {     transform: translate(0, -408px) scale(4, 3.55);   } } 

This process is harder when the tool generating the SVG code attempts to “simplify” the transform into a matrix. While you can recreate the matrix transform by copying it into the CSS, it is a difficult task to do. You’re a better developer than me — which might be true anyway — if you can take a matrix transform and manipulate it to scale, rotate, or translate in the exact way you want.

Alternatively, you can recreate the matrix transform using translation, rotation, and scaling, but if the path is complex, the likelihood that you can recreate it in a timely manner without finding yourself in a straight jacket is low. 

The last and probably easiest option is to wrap the element in a group (<g>) tag. Add a class or ID to it for easy CSS access and transform the group itself, thus separating out the transforms as discussed in the last lesson. 

Lesson 8: Keep your sanity by using transform-origin when transforming part of an SVG

The CSS transform-origin property moves the point around which the transform happens. If you’re trying to rotate an arm — like I did in Clubbin’ It —  your animation will look more natural if you rotate the arm from the center of the shoulder, but that path’s natural transform origin is in the upper-left. Use transform-origin to fix this for smoother, more natural feel… you know that really natural pixel art look…

Four sequential frames of an animation showing a caveman character facing left, holding a large wooden club, and raising it up from the bottom to behind his head.

Transforming the origin can also be useful when scaling, like I did in Mustachioed Oops, or when rotating mouth movements, such as the dinosaur’s jaw in Super Tasty. If you don’t change the origin, the transforms will use an origin point at the upper left corner of the SVG element. 

Lesson 9: Sprite animations can be responsive

I ended up doing a lot of sprite animations for this project (i.e., where you use multiple, incremental frames and switch between them fast enough that the characters seem to move). I created the images in one wide file, added them as a background image to an element the size of a single frame, used background-size to set the background image to the width of the image, and hid the overflow. Then I used background-position and the animation timing function, step(), to walk through the images; for example: Post-Apocalyptic Celebrations.

Before the project, I always used inflexible images. I’d scale things down a little so that there would be at least a little responsive give, but I didn’t think you could make it a fully flexible width. However, if you use SVG as the background image you can then use viewport units to scale the element along with the changing screen size. The only problem is the background position. However, if you use viewport units for that, it will stay in sync. Check that out in Finally, Alone with my Sandwich…

Lesson 9A: Use viewport units to set the background size of an image when creating responsive sprite animation

As I’ve learned throughout this project, using a single type of unit  is almost always the way to go. Initially, I’d set my sprite’s background size using percentages. The math was easy (100% * (number of steps + 1)) and it worked fine in most cases. In longer animations, however, the exact frame tracking could be off and parts of the wrong sprite frame might display. The problem grows as more frames are added to the sprite. 

I’m not sure the exact reason this causes an issue, but I believe it’s because of rounding errors that compound over the length of the sprite sheet (the amount of the shift increases with the number of frames). 

For my final animation, It Ain’t Over Till the Zombie Sings, I had a dinosaur open his mouth to reveal a zombie Viking singing (while lasers fired in the background plus there was dancing, accordions playing and zombies fired from cannons, of course). Yeah, I know how to throw a party… a nerd party.

The dinosaur and viking was one of the longest sprite animations I did for the project. But when I used percentages to set the background size, the tracking would be off at certain sizes in Safari. By the end of the animation, part of the dinosaur’s nose from a different frame would appear to the right and a similar part of the nose would be missing on the left.

A large green dinosaur behind a crowd of people, all facing and looking forward.
The dinosaur on the left is missing part of his left cheek and growing a new one next to his right cheek.

This was super frustrating to diagnose because it seemed to work fine in Chrome and I’d think I fixed it in Safari only to look at a slightly different screen size and see the frame off again. However, if I used consistent units — i.e. vw for background-size, frame width, and background-position — everything worked fine. Again, it comes down to working with consistent units!

Lesson 10: Invite people into the project.

A crowd of 32 pixel-art characters from the previous demos facing the screen.

While I learned tons of things during this process, I beat my head against the wall for most of it (often until the wall broke or my head did… I can’t tell). While that’s one way to do it, even if you’re hard-headed, you’ll still end up with a headache. Invite others into your project, be it for advice, to point out an obvious blind spot you missed, provide feedback, help with the project, or simply to encourage you to keep going when the scope is stupidly and arbitrarily large. 

So let me put this lesson into practice. What are your thoughts? How will you stop the zombie hordes with CSS animation? What stupidly and arbitrarily large project will you take on to stretch yourself?


The post Lessons Learned from Sixty Days of Re-Animating Zombies with Hand-Coded CSS appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , , ,

Excluding Emojis From Transparent Text Clipping

CSS-Tricks has this pretty cool way of styling hovered links. By default, the text is a fairly common blue. But hover of the links, and they’re filled with a linear gradient.

😍

Pretty neat, right? And the trick isn’t all that complicated. On hover…

  • give the link a linear gradient background,
  • clip the background to the text, and
  • give the text a transparent fill so the background shows through.

It looks like this in CSS:

a {   color: #007db5; }  a:hover {   background: linear-gradient(90deg,#ff8a00,#e52e71);   -webkit-background-clip: text;   -webkit-text-fill-color: transparent; }

Notice the -webkit- prefix, which is required for now. There’s a little more to the actual implementation here on CSS-Tricks, but this little bit gets us what we’re looking for.

But that’s not the point here. Just the other day, Brad Westfall phoned in to let us know that this technique also takes effect on emojis which, like any other text, gets a transparent fill on hover.

He noticed it happening on a link in one of our posts.

Not the worst thing. And it totally makes sense. I mean, an emoji is a glyph like any other text in a font file, right? They just happen to be a color font and take on the form of an image. Of course they would be treated like any other glyph in a situation like this where we’re hallowing out the fill color.

But if keeping the color in tact on emojis is a requirement, that can be resolved by wrapping the emoji in a span and setting its fill back to its initial state.

But who wants to write a span every time an emoji happens to pop up in a link? 👎

If you’re looking for a CSS solution, we’re kinda out of luck. That said, the CSS Fonts Module Level 4 specification includes a definition for a proposed font-variation-emoji property. However, there’s not much on it (that I can find) at the moment and it doesn’t appear to be designed for this sort of thing, A quick skim of some discussion related to the proposal suggests it’s more about the way some browsers or systems automatically convert Unicode to emoji and how to control that behavior.

There’s also the proposed definition of font-palette in the same draft spec which seems like a way to control color fonts — that’s what emojis are at the end of the day. But this isn’t the solution, either.

It seems the only way to prevent an emoji’s fill from being changed without a span is some sort of JavaScript solution. Look at services like WordPress, Dropbox, Facebook and Twitter. They all implement their own custom emoji sets. And what do they use? Images.

Yeah, along with a lot of divs and such!

That would be one way to do it. If the emoji is replaced with an image (an SVG in this specific example), then that would certainly exclude it from being filled along with the link text.

Or, hey, why not prevent ourselves from getting into the situation at all and place that dang thing outside of the link?

That’s probably the route we should have taken all along. But an emoji might not come at the beginning or end of a link, but somewhere in the middle. It just underscores the point that there are cases where having some sort of control here could come in handy.


The post Excluding Emojis From Transparent Text Clipping appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

Can you get valid CSS property values from the browser?

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That’s like this.

That gives you, for example, the fact that cursor is a thing. But then how do you know what valid values are for cursor? We know from documentation that there are values like auto, none, help, context-menu, pointer, progress, wait, and many more.

But where does that list come from? Well, there is a list right in the spec so that’s helpful. But that doesn’t guarantee the complete list of values that any given browser actually supports. There could be cursor: skull-and-crossbones and we wouldn’t even know!

We can test by applying it to an element and looking in DevTools:

Damn.

But unless we launch a huge dictionary attack against that value, we don’t actually know what values it directly in-browser. Maybe Houdini will help somehow in browsers getting better at CSS introspection?

You can also use the CSS object to run tests like CSS.supports(property, value):

Damn.

You’d think we could have like CSS.validValues("text-decoration-thickness") and get like ["<length>", "<percentage>", "auto", "from-font"] or the like, but alas, not a thing.


The post Can you get valid CSS property values from the browser? appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

How to delete all node_modules directories from your computer

Nice tip from Chris Ferdinandi:

My node_modules directories contained 50mb of stuff on the small side, and over 200mb of files in some cases. Over a few dozen projects, that really adds up!

Two dozen projects with 200 MB worth of node_modules? That’s nearly 5 GB of space for a bunch of stuff you’ve probably forgotten is even there, isn’t doing anything, and if you need again is a single command away. I feel like there should almost be a reaper app for these folders, deleting them if they haven’t been touched in a few weeks.

Nuke ’em:

# Mac/Linux find . -name "node_modules" -type d -prune -print | xargs du -chs  # Windows FOR /d /r . %d in (node_modules) DO @IF EXIST "%d" echo %d"

Direct Link to ArticlePermalink

The post How to delete all node_modules directories from your computer appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Five 5-minute Videos from Ethan on Design & Accessibility

Ethan:

I’ve been working with Aquent Gymnasium to produce a series of five short tutorial videos, which have been launching over the course of this past week. Since the last video just went live, I’m thrilled to share the whole list with you:

Introduction to using VoiceOver on macOS
Designing beautiful focus states
Flexible and accessible typesetting
Responsively designing with viewport units
Designing beautiful and accessible drop caps

Five minutes is a real sweet spot for a how-to video. Ain’t no time to screw around. I loved every minute of these.

Direct Link to ArticlePermalink

The post Five 5-minute Videos from Ethan on Design & Accessibility appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Learn Eleventy From Scratch

The latest edition of Andy Bell’s Piccalilli landed in my inbox this morning with a sweet offer: preorder Andy’s course on learning Eleventy from scratch at a third of the price.

Why the plug? No, not sponsorships or anything like that. I just happen to hear a heckuva lot about Eleventy these days. Like how we can use it with Google Sheets as a pseudo-CMS. Or how it can be a key component of an emergency website kit. I mean, geez, Chris even used it for the conferences site we have around here. As Andy says, “the future is bright because the future is static.” At least, it certainly appears that way.

I’m squarely in the novice camp when it comes to Eleventy, not to mention static site generators as a whole. That’s why I signed up for the course. It promises to be a deep dive that starts with an empty directory and goes all the way to full-blown website. Given that Andy has created more Eleventy sites than most folks (seriously, it’s documented) and that his Eleventy-powered Piccalilli site notches perfect Lighthouse scores, I think he’ll have a lot to offer in a course.

While we’re on the topic of Eleventy, there are other guides, tutorials and courses out there you might find compelling:

Direct Link to ArticlePermalink

The post Learn Eleventy From Scratch appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

CSS Findings From The New Facebook Design

Ahmad Shadeed digs around the new Facebook’s front-end code.

One that stood out to me:

.element {   inset: 4px 0;   /* Which is equivalent to: top: 4px, bottom: 4px, left: 0, right: 0 */ }

Whaaat? This is the first I’ve heard of the inset property. Ahmad said he saw it working in Chrome 80, but it definitely isn’t for me (nor Safari). It does in Firefox though.

Chrome 80 and Firefox 75

It’s shorthand for top/right/bottom/left (just like margin, in a sense), which is certainly welcome if you ask me. Bring on the support.


If you’re into people digging into big sites to learn stuff…

Direct Link to ArticlePermalink

The post CSS Findings From The New Facebook Design appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Moving from Vanilla JavaScript to a Reusable Vue Component

I recently wrote an article explaining how you can create a countdown timer using HTML, CSS and JavaScript. Now, let’s look at how we can make that a reusable component by porting it into Vue using basic features that the framework provides.

Why do this at all? Well there are few reasons, but two stand out in particular:

  • Keeping UI in sync with the timer state: If you look at the code from the first post,  it all lives in the timerInterval function, most noticeably the state management. Each time it runs (every second) we need to manually find the proper element on our document — whether it’s the time label or the remaining time path or whatever — and change either its value or an attribute. Vue comes with an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. That takes all the burden of finding and updating proper UI elements so we can rely purely on the component instance’s properties.
  • Having a highly reusable component: The original example works fine when only one timer is present on our document, but imagine that you want to add another one. Oops! We rely the element’s ID to perform our actions and using the same ID on multiple instances would prevent them from working independently. That means we would have to assign different IDs for each timer. If we create a Vue component, all it’s logic is encapsulated and connected to that specific instance of the component. We can easily create 10, 20, 1,000 timers on a single document without changing a single line in the component itself!

Here’s the same timer we created together in the last post, but in Vue.

Template and styles

From the Vue docs:

Vue uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. All Vue.js templates are valid HTML that can be parsed by spec-compliant browsers and HTML parsers.

Let’s create our component by opening a new file called BaseTimer.vue. Here’s the basic structure we need for that:

// Our template markup will go here <template> // ... </template>  // Our functional scripts will go here <script> // ... </script>  // Our styling will go here <style> // ... </style>

In this step, we will concentrate on the <template> and <style> sections. Let’s move our timer template to the <template> section and all our CSS to <style> section. The markup mostly consists of SVG and we can use the exact same code we used from the first article.

<template>   // The wrapper for the timer   <div class="base-timer">      // This all comes from the first article     <svg class="base-timer__svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">       <g class="base-timer__circle">         <circle class="base-timer__path-elapsed" cx="50" cy="50" r="45"></circle>         <path           id="base-timer-path-remaining"           stroke-dasharray="283"           class="base-timer__path-remaining $  {remainingPathColor}"           d="             M 50, 50             m -45, 0             a 45,45 0 1,0 90,0             a 45,45 0 1,0 -90,0           "         ></path>       </g>     </svg>      // The label showing the remaining time     <span       id="base-timer-label"       class="base-timer__label"     >       $  {formatTime(timeLeft)}     </span>    </div> </template>  // "scoped" means these styles will not leak out to other elements on the page <style scoped> .base-timer {   position: relative;   width: 100px;   height: 100px; } </style>

Let’s have a look at the template we just copied to identify where we can use our framework. There are few parts that are responsible for making our timer count down the time and show the remaining time.

  • stroke-dasharray: A value passed to the SVG <path> element that is responsible for holding the remaining time.
  • remainingPathColor: A CSS class responsible for changing the color of the timer’s circular ring, giving is a way to visually indicate that time is running out.
  • formatTime(timeLeft): A value responsible for showing how much time is left inside the timer

We can control our timer by manipulating those values.

Constants and variables

OK, let’s go down to our <script> section and see what Vue gives us out of the box to make our life easier. One thing it lets us do is define our constants up front, which keeps them scoped to the component.

In the last post, we spent a little time tweaking the stroke-dasharray  value to make sure the animation of the timer’s top layer (the ring that animates and changes color as time progresses) is perfectly in line with its bottom layer (the gray ring that indicates past time). We also defined “thresholds” for when the top layer should change colors (orange at 10 remaining seconds and red at five seconds). We also created constants for those colors.

We can move all of those directly into the <script> section:

<script> // A value we had to play with a bit to get right const FULL_DASH_ARRAY = 283; // When the timer should change from green to orange const WARNING_THRESHOLD = 10; // When the timer should change from orange to red const ALERT_THRESHOLD = 5;  // The actual colors to use at the info, warning and alert threshholds const COLOR_CODES = {   info: {     color: "green"   },   warning: {     color: "orange",     threshold: WARNING_THRESHOLD   },   alert: {     color: "red",     threshold: ALERT_THRESHOLD   } };  // The timer's starting point const TIME_LIMIT = 20; </script>

Now, let’s have a look at our variables:

let timePassed = 0; let timeLeft = TIME_LIMIT; let timerInterval = null; let remainingPathColor = COLOR_CODES.info.color;

We can identify two different types of variables here:

  1. Variables in which the values are directly re-assigned in our methods:
    • timerInterval: Changes when we start or stop the timer
    • timePassed: Changes each second when the timer is running
  2. Variables in which the values change when other variables change:
    • timeLeft: Changes when the value of timePassed changes
    • remainingPathColor: Changes when the value of timeLeft breaches the specified threshold

It is essential to identify that difference between those two types as it allows us to use different features of the framework. Let’s go through each of the type separately.

Variables in which values are directly re-assigned

Let’s think what we want to happen when we change the timePassed value. We want to calculate how much time is left, check if we should change the top ring’s color, and trigger re-render on a part of our view with new values. 

Vue comes with its own reactivity system that updates the view to match the new values of specific properties. To add a property to Vue’s reactivity system we need to declare that property on a data object in our component. By doing that,Vue will create a getter and a setter for each property that will track changes in that property and respond accordingly.

<script> // Same as before  export default {   data() {     return {       timePassed: 0,       timerInterval: null     };   } </script>

There are two important things we need to remember.

  1. We need to declare all reactive variables in our data object up front. That means if we know that a variable will exist but we don’t know what the value will be, we still need to declare it with some value. If we forgot to declare it in data it will not be reactive, even if it is added later.
  2. When declaring our data option object, we always need to return a new object instance (using return). This is vital because, if we don’t follow this rule, the declared properties will be shared between all instances of the component.

You can see that second issue in action:

Variables in which values change when other variable change

These variables rely on the value of another variable. For example, timeLeft relies purely on timePassed. In our original example that uses vanilla JavaScript, we were calculating that value in the interval that was responsible for changing the value of timePassed. With Vue, we can extract that value to a computed property.

computed property is a function that returns a value. These values are bound to the dependency values and only update when required. Even more importantly, computed properties are cached, meaning they remember the values that the computed property depends on and calculate the new value only if that dependent property value changed. If the value does not change, the previously cached value is returned.

<script> // Same as before  computed: {     timeLeft() {       return TIME_LIMIT - this.timePassed;     }   } } </script>

The function passed to the computed property must be a pure function. It can’t cause any side effects and must return a value. Also, the output value must only be dependent on the values passed into the function.

Now, we can move more logic to computed properties:

  • circleDasharray: This returns a value previously that is calculated in the setCircleDasharray method.
  • formattedTimeLeft: This returns a value from the formatTime method.
  • timeFraction: This is an abstraction of the calculateTimeFraction method.
  • remainingPathColor: This is an abstraction of the setRemainingPathColor method.
<script> // Same as before  computed: {     circleDasharray() {       return `$  {(this.timeFraction * FULL_DASH_ARRAY).toFixed(0)} 283`;     },      formattedTimeLeft() {       const timeLeft = this.timeLeft;       const minutes = Math.floor(timeLeft / 60);       let seconds = timeLeft % 60;       if (seconds < 10) {         seconds = `0$  {seconds}`;       }       return `$  {minutes}:$  {seconds}`;     },      timeLeft() {       return TIME_LIMIT - this.timePassed;     },      timeFraction() {       const rawTimeFraction = this.timeLeft / TIME_LIMIT;       return rawTimeFraction - (1 / TIME_LIMIT) * (1 - rawTimeFraction);     },      remainingPathColor() {       const { alert, warning, info } = COLOR_CODES;       if (this.timeLeft <= alert.threshold) {         return alert.color;       } else if (this.timeLeft <= warning.threshold) {         return warning.color;       } else {         return info.color;       }     }   } </script>

We now have all the values we need! But now we need to put them to use in our template.

Using data and computed properties in the template

Here’s where we left off with our template:

 <template>   <div class="base-timer">     <svg class="base-timer__svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">       <g class="base-timer__circle">         <circle class="base-timer__path-elapsed" cx="50" cy="50" r="45"></circle>         <path           id="base-timer-path-remaining"           stroke-dasharray="283"           class="base-timer__path-remaining $  {remainingPathColor}"           d="             M 50, 50             m -45, 0             a 45,45 0 1,0 90,0             a 45,45 0 1,0 -90,0           "         ></path>       </g>     </svg>     <span       id="base-timer-label"       class="base-timer__label"     >         $  {formatTime(timeLeft)}     </span>   </div> </template>

Let’s start with formatTime(timeLeft). How we can dynamically bind the rendered value to our formattedTimeLeftcomputed property?

Vue uses HTML-based template syntax that allowsus to declaratively bind the rendered DOM to the underlying data of the Vue instance. That means all properties are available in the template section. To render any of them, we use text interpolation using the “Mustache” syntax (double curly braces, or {{ }}).

<span   id="base-timer-label"   class="base-timer__label" >   {{ formattedTimeLeft }}  </span>

Next will be stroke-dasharray. We can see we don’t want to render that value. Instead, we want to change the value of the <path> attribute. Mustache cannot be used inside HTML attributes, but fear not! Vue comes with another way: the v-bind directive. We can bind a value to an attribute like this:

<path  v-bind:stroke-dasharray="circleDasharray"></path>

To facilitate the usage of that directive, we can also use a shorthand.

<path  :stroke-dasharray="circleDasharray"></path>

The last one is remainingPathColor, which adds a proper class to an element. We can do that using the same v-bind directive as above, but assign the value to the class attribute of an element.

<path  :class="remainingPathColor"></path>

Let’s have a look at our template after changes.

<template>   <div class="base-timer">     <svg class="base-timer__svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">       <g class="base-timer__circle">         <circle class="base-timer__path-elapsed" cx="50" cy="50" r="45"></circle>         <path           :stroke-dasharray="circleDasharray"           class="base-timer__path-remaining"           :class="remainingPathColor"           d="             M 50, 50             m -45, 0             a 45,45 0 1,0 90,0             a 45,45 0 1,0 -90,0           "         ></path>       </g>     </svg>     <span class="base-timer__label">{{ formattedTimeLeft }}</span>   </div> </template>

We have our template ready, we moved all variables to data or computed, and we got rid off most of the methods by creating corresponding computed properties. We are still missing one vital part, though: we need to start our timer.

Methods and component lifecycle hooks

If we look at our startTimer method, we can see that all the calculations, changes in attributes, etc. happen in the interval.

function startTimer() {   timerInterval = setInterval(() => {     timePassed = timePassed += 1;     timeLeft = TIME_LIMIT - timePassed;     document.getElementById("base-timer-label").innerHTML = formatTime(       timeLeft     );     setCircleDasharray();     setRemainingPathColor(timeLeft);     if (timeLeft === 0) {       onTimesUp();     }   }, 1000); }

Since we’ve already moved all that logic into the computed property, all we need to do in our timerInterval is change the value of timePassed — the rest will happen magically in the computed properties

<script> // Same as before  methods: {   startTimer() {     this.timerInterval = setInterval(() => (this.timePassed += 1), 1000);   } } </script>

We have the method ready, but we still don’t call it anywhere. Each Vue component comes with a series of hooks that allows us to run a specific logic within a specific period of the component’s lifecycle. These are called lifecycle hooks. In our case, as we want to call our method immediately when the component gets loaded. That makes mounted the lifecycle hook what we want.

<script> // Same as before  mounted() {   this.startTimer(); },  // Same methods as before </script> 

That’s it, we just turned our timer into a consistent and reusable component using Vue!

Let’s say we now want to use this component in another component. That requires a few things:

  1. First, we import the component.
  2. Next, we register the component.
  3. Finally, we instantiate the component in the template.
// App.vue  import BaseTimer from "./components/BaseTimer"  export default {   components: {     BaseTimer   } }; 

That’s a wrap!

This example shows how we can move a component from vanilla JavaScript to a component-based front-end framework, like Vue. 

We can now treat the timer as a standalone component where all the markup, logic and styling is contained in a way that won’t leak out to or conflict with other elements. Components are often children of a larger parent component that assembles multiple components together — like a form or perhaps a card — where the parent’s properties can be accessed and shared. Here’s an example of the timer component where it’s taking orders from a parent component

I hope I got you interested in Vue and the power of components! I’d encourage you to go to Vue docs to get more detailed description of the features we used in our example. There’s so much Vue can do!

The post Moving from Vanilla JavaScript to a Reusable Vue Component appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

What to Expect from the JAMstack in 2020

Brian Rinaldi interviewed a variety of folks, asking them the same questions about JAMstack development and the landscape recently:

  • Raymond Camden: I think we will see better competition from the bigger players.
  • Gift Egwuenu: I’m also looking forward to more job openings on the JAMstack.
  • Bryan Robinson: If you find yourself jumping through too many hoops, it might be time to explore a monolith architecture again.
  • Me: Blah blah blah, read the other ones from smart people 🙂
  • Tara Manicsic: there are more examples of enterprise applications creating more reliable user experiences, faster response times, and cutting their costs.

The post What to Expect from the JAMstack in 2020 appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Clips from my DEV AMA

I recently did an AMA over on DEV. Just taking the opportunity to port over some answers here like a good indiewebber.

If you were starting out as a front end dev in 2020, what would you say is the first thing you would learn and why?

You need to put yourself in a position where it’s your job to create and take care of a website. Even if that feels like a stretch for you early on. Get the domain, get the website on the public internet. Put your name on it. Now you’ve given yourself stakes, and you’ll learn technology because you must make your ideas come to life.

For me, 650 years ago, that was putting up a website for the ol’ college band. We needed a website! That sounded like fun to me, and I managed to struggle through buying a domain, hosting, and putting up a WordPress website. Then, over time, I learned front-end web technologies because I wanted to change up the design, change up the templates, add cool features, etc.

Get yourself a project and learn through the project.

How do you determine what you want to turn into a blog post and what you leave as a simple Tweet?

I usually won’t avoid the tweet. The tweet is usually a good proving ground for the blog post anyway. If nobody cared, eh, maybe not that good of a post. If it does get good engagement, it’s like the conversation around it is useful in the creation of the blog post. Plus, tweets are so easy to kick out the door. Blog posts, for me, on purpose, have a longer schedule that includes editing and scheduling and such.

Here’s an example tweet. Just a silly little UI experiment. I didn’t feel like waiting to blog about it to drop the demo. But from the Twitter thread, I got some interesting technical feedback, info about what parts people were most suprised by, and some other related ideas. That will, hopefully, lead to a much more robust blog post.

I even treat DEV like that, honestly. I wrote this blog post reaction quickly here, but then refined it for my own blog with some of the feedback.

Do you have a favorite CSS-Trick, where you were just like “wow”?

I think “scroll shadows” in CSS is one of my favorite CSS tricks of all time. It’s originally by Roman Komarov, but explained and improved by Lea Verou. I saw a tool the other day around the idea by Stefan Judis.

It’s a real mind-bender involving four-layered gradient backgrounds, each positioned, sized, and colored differently, and then behaviorally different regarding scrolling.

It’s not just a neat trick because it has real UX implications. Showing a shadow of where you can scroll is important UX. Consider this story of a recent design update in iOS that led to complete confusion around UI actions hidden behind a place you could scroll to, but had zero affordance on how to get there. (Happens to me all the time in Spotify, for the record.)

What would be your top 3 pieces of quick advice for developers trying to follow a similar path to growing their influence and exposure?

I think writing is literally the only way.

I can’t think of a developer with influence who has that influence for anything other than writing. Or if it’s not writing, then it’s a YouTube channel or some other form of creating public stuff.

How much do you see yourself personally playing with Houdini APIs as they are released? Which API are you most excited about (Painting, Layout, Typed OM, …)?

This super-low level stuff sometimes feels over my head. It’s hard for me to picture the industry implications of stuff like this just by looking at specs, ya know?

To me, it seems the Layout API has the most powerful potential.

What I’m imagining right now is that Houdini doesn’t affect normal day-to-day front-end developers like me that much. I won’t be writing much Houdini code. But I’ll use fancy things that other people create, because it does something useful for me. Just like most people don’t write their own libraries or have published npm packages — they just use them.

It’s fun to be wow’d by Houdini. If anyone is looking for that, make sure to look at Vincent De Oliveira’s showcase website.

What is your favorite thing about working at CodePen and/or CSS-Tricks?

You know what I really like? I like getting into the office every day and having a pretty decent amount of freedom of what I’m going to do that day. I’ll probably have meetings. I’ll probably have some stuff on the ol’ calendar. I’ll probably have some team expectations I’m trying to meet. But I also usually have plenty of time to pursue things that are interesting to me at the moment.

Sometimes I’m in the moment to drill through some emails. Sometimes I want to tinker with some demo that sounds like fun. Sometimes I want to write up a thought or record a video. Sometimes I want to plan something out or document something. Sometimes I want to talk something out with other people or do some pair programming.

I’m fortunate that I’m the boss (lol) and I put myself in that position on purpose so I have that freedom.

What is something that you wish we could add in CSS?

I feel like every time someone asks this we all should take every opportunity to scream Container Queries! until we get them.

The idea is that we should be able to write CSS that says, “When this element is this wide, this CSS should take effect.” And not just width, but whatever media queries we have at the page level already.

The best demo of a use case out there is Philip Walton’s page.

I want to write a card component that shuffles itself around based on how wide it is, not how wide the page is, because there isn’t always a direct connection between those two things (e.g. a card component can show up in a narrow sidebar on a large screen, but be full-width on a tablet or something).

Every component can be in a situation like that, so for the love of CSS, let me write media queries scoped to those components. I echo a lot of other people when I saw that if we had this, the vast majority of media queries we write would be these, not page-level.

Do you think it’s worth suggesting a { position: above-fold; }?

I’m not sure I’ve ever made a big fold-based decision once in my career. Not a big fan of that thinking. THERE IS A LINE IN WHICH THIS IMPORTANT MODULE MUST NOT CROSS, haha. Prioritizing the most important stuff to be higher up the page, sure. Websites don’t fold like newspapers.

Plus, we’ve got viewport units now, so if you absolutely need to position something in the top visible viewport area, you can.

Since you’ve been writing blog posts for so long, have you developed a process for writing one?

Sorta! It still feels pretty casual to me (let’s call my writing medium quality), so it’s not like I’m renting a cabin in the wilderness and finding inspiration in the sunsets and cheap whiskey.

  • I write down every blog post idea that comes to me. I try to keep that list fairly public but I also have a personal list where I can be even sloppier.
  • I put as much context into those lists as I can, so I can hope to summon up the same emotion that made me write it down in the first place. If I revisit the idea a week later and can’t, it’s probably not a very good idea.
  • I write up the post with as much context as I can. Light research is typically involved.
  • We have a lead editor on CSS-Tricks, so it’s reviewed by at least one person before being scheduled.

CSS or CSS-in-JS?

I see a ton of cool stuff happening in CSS-in-JS. I think it solves a lot of interesting problems for certain websites. For example, I very much like the idea of having the option to write styles that are scoped to a component programmatically, and thus are tree-shaken when the component isn’t used automatically.

But the web is a big place, and dare I say most websites aren’t built with JavaScript-powered component models. Thus, CSS-in-JS isn’t necessary or appropriate for a lot of sites.

Although, two things to be clear:

  • You can’t have CSS-in-JS without CSS. CSS-in-JS is still styles that are applied to elements. It doesn’t absolve you from learning CSS.
  • The CSS-in-JS landscape is wide. It’s a little hard to talk about so vaguely. Each project in the bucket of CSS-in-JS handles things a bit differently and how the styles are applied to the site is even quite wide. I think it sometimes gets lost in the arguments that some of the approaches literally make a CSS stylesheet that you link up like you would any other CSS — even Sass-produced CSS — which there doesn’t seem to be much argument about anymore.

The post Clips from my DEV AMA appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]