Tag: time

Hacking CSS Animation State and Playback Time

CSS-only Wolfenstein is a little project that I made a few weeks ago. It was an experiment with CSS 3D transformations and animations.

Inspired by the FPS demo and another Wolfenstein CodePen, I decided to build my own version. It is loosely based on Episode 1 – Floor 9 of the original Wolfenstein 3D game.

Editor: This game intentionally requires some quick reaction to avoid a Game Over screen.

Here is a playthrough video:

In a nutshell, my project is nothing but a carefully scripted long CSS animation. Plus a few instances of the checkbox hack.

:checked ~ div { animation-name: spin; }

The environment consists of 3D grid faces and the animations are mostly plain 3D translations and rotations. Nothing really fancy.

However, two problems were particularly tricky to solve:

  • Play the “weapon firing” animation whenever the player clicks on an enemy.
  • When the fast-moving boss got the last hit, enter a dramatic slow motion.

At a technical-level, this meant:

  • Replay an animation when the next checkbox is checked.
  • Slow down an animation, when a checkbox is checked.

In fact, neither was properly solved in my project! I either ended up using workarounds or just gave up.

On the other hand, after some digging, eventually I found the key to both problems: altering the properties of running CSS animations. In this article, we will explore further on this topic:

  • Lots of interactive examples.
  • Dissections: how does each example work (or not work)?
  • Behind-the-scene: how do browsers handle animation states?

Let me “toss my bricks”.

Problem 1: Replaying Animation

The first example: “just another checkbox”

My first intuition was “just add another checkbox”, which does not work:

Each checkbox works individually, but not both together. If one checkbox is already checked, the other no longer works.

Here’s how it works (or “does not work”):

  1. The animation-name of <div> is none by default.
  2. The user clicks on one checkbox, animation-name becomes spin, and the animation starts from the beginning.
  3. After a while, the user clicks on the other checkbox. A new CSS rule takes effect, but animation-name is still spin, which means no animation is added nor removed. The animation simply continues playing as if nothing happened.

The second example: “cloning the animation”

One working approach is to clone the animation:

#spin1:checked ~ div { animation-name: spin1; } #spin2:checked ~ div { animation-name: spin2; }

Here’s how it works:

  1. animation-name is none initially.
  2. The user clicks on “Spin!”, animation-name becomes spin1. The animation spin1 is started from the beginning because it was just added.
  3. The user clicks on “Spin again!”, animation-name becomes spin2. The animation spin2 is started from the beginning because it was just added.

Note that in Step #3, spin1 is removed because of the order of the CSS rules. It won’t work if “Spin again!” is checked first.

The third example: “appending the same animation”

Another working approach is to “append the same animation”:

#spin1:checked ~ div { animation-name: spin; } #spin2:checked ~ div { animation-name: spin, spin; }

This is similar to the previous example. You can actually understand the behavior this way:

#spin1:checked ~ div { animation-name: spin1; } #spin2:checked ~ div { animation-name: spin2, spin1; }

Note that when “Spin again!” is checked, the old running animation becomes the second animation in the new list, which could be unintuitive. A direct consequence is: the trick won’t work if animation-fill-mode is forwards. Here’s a demo:

If you wonder why this is the case, here are some clues:

  • animation-fill-mode is none by default, which means “The animation has no effect at all if not playing”.
  • animation-fill-mode: forwards; means “After the animation finishes playing, it must stay at the last keyframe forever”.
  • spin1’s decision always override spin2’s because spin1 appears later in the list.
  • Suppose the user clicks on “Spin!”, waits for a full spin, then clicks on “Spin again!”. At this moment. spin1 is already finished, and spin2 just starts.

Discussion

Rule of thumb: you cannot “restart” an existing CSS animation. Instead, you want to add and play a new animation. This may be confirmed by the W3C spec:

Once an animation has started it continues until it ends or the animation-name is removed.

Now comparing the last two examples, I think in practice, “cloning animations” should often work better, especially when CSS preprocessor is available.

Problem 2: Slow Motion

One might think that slowing an animation is just a matter of setting a longer animation-duration:

div { animation-duration: 0.5s; } #slowmo:checked ~ div { animation-duration: 1.5s; }

Indeed, this works:

… or does it?

With a few tweaks, it should be easier to see the issue.

Yes, the animation is slowed down. And no, it does not look good. The dog (almost) always “jumps” when you toggle the checkbox. Furthermore, the dog seems to jump to a random position rather than the initial one. How come?

It would be easier to understand it if we introduced two “shadow elements”:

Both shadow elements are running the same animations with different animation-duration. And they are not affected by the checkbox.

When you toggle the checkbox, the element just immediately switches between the states of two shadow elements.

Quoting the W3C spec:

Changes to the values of animation properties while the animation is running apply as if the animation had those values from when it began.

This follows the stateless design, which allows browsers to easily determine the animated value. The actual calculation is described here and here.

Another Attempt

One idea is to pause the current animation, then add a slower animation that takes over from there:

div {   animation-name: spin1;   animation-duration: 2s; }  #slowmo:checked ~ div {   animation-name: spin1, spin2;   animation-duration: 2s, 5s;   animation-play-state: paused, running; }

So it works:

… or does it?

It does slow down when you click on “Slowmo!”. But if you wait for a full circle, you will see a “jump”. Actually, it always jumps to the position when “Slowmo!” is clicked on.

The reason is we don’t have a from keyframe defined – and we shouldn’t. When the user clicks on “Slowmo!”, spin1 is paused at some position, and spin2 starts at exactly the same position. We simply cannot predict that position beforehand … or can we?

A Working Solution

We can! By using a custom property, we can capture the angle in the first animation, then pass it to the second animation:

div {   transform: rotate(var(--angle1));   animation-name: spin1;   animation-duration: 2s; }  #slowmo:checked ~ div {   transform: rotate(var(--angle2));   animation-name: spin1, spin2;   animation-duration: 2s, 5s;   animation-play-state: paused, running; }  @keyframes spin1 {   to {     --angle1: 360deg;   } }  @keyframes spin2 {   from {     --angle2: var(--angle1);   }   to {     --angle2: calc(var(--angle1) + 360deg);   } }

Note: @property is used in this example, which is not supported by all browsers.

The “Perfect” Solution

There is a caveat to the previous solution: “exiting slowmo” does not work well.

Here is a better solution:

In this version, slow motion can be entered or exited seamlessly. No experimental feature is used either. So is it the perfect solution? Yes and no.

This solution works like “shifting” “gears”:

  • Gears: there are two <div>s. One is the parent of the other. Both have the spin animation but with different animation-duration. The final state of the element is the accumulation of both animations.
  • Shifting: At the beginning, only one <div> has its animation running. The other is paused. When the checkbox is toggled, both animations swap their states.

While I really like the result, there is one problem: it is a nice exploit of the spin animation, which does not work for other types of animations in general.

A Practical Solution (with JS)

For general animations, it is possible to achieve the slow motion function with a bit of JavaScript:

A quick explanation:

  • A custom property is used to track the animation progress.
  • The animation is “restarted” when the checkbox is toggled.
  • The JS code computes the correct animation-delay to ensure a seamless transition. I recommend this article if you are not familiar with negative values of animation-delay.

You can view this solution as a hybrid of “restarting animation” and the “gear-shifting” approach.

Here it is important to track the animation progress correctly. Workarounds are possible if @property is not available. As an example, this version uses z-index to track the progress:

Side-note: originally, I also tried to create a CSS-only version but did not succeed. While not 100% sure, I think it is because animation-delay is not animatable.

Here is a version with minimal JavaScript. Only “entering slowmo” works.

Please let me know if you manage to create a working CSS-only version!

Slow-mo Any Animation (with JS)

Lastly, I’d like to share a solution that works for (almost) any animation, even with multiple complicated @keyframes:

Basically, you need to add an animation progress tracker, then carefully compute animation-delay for the new animation. However, sometimes it could be tricky (but possible) to get the correct values.

For example:

  • animation-timing-function is not linear.
  • animation-direction is not normal.
  • multiple values in animation-name with different animation-duration’s and animation-delay’s.

This method is also described here for the Web Animations API.

Acknowledgments

I started down this path after encountering CSS-only projects. Some were delicate artwork, and some were complex contraptions. My favorites are those involving 3D objects, for example, this bouncing ball and this packing cube.

In the beginning, I had no clue how these were made. Later I read and learned a lot from this nice tutorial by Ana Tudor.

As it turned out, building and animating 3D objects with CSS is not much different from doing it with Blender, just with a bit different flavor.

Conclusion

In this article we examined the behavior of CSS animations when an animate-* property is altered. Especially we worked out solutions for “replaying an animation” and “animation slow-mo”.

I hope you find this article interesting. Please let me know your thoughts!


Hacking CSS Animation State and Playback Time originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , ,

PHP Date and Time Recipes

Dealing with dates and times is one of those things that can frustrate programmers a lot. At the same time, they are fundamental to software development, used from everything from meta and how things are ordered to time-based triggers and lots in between.

Dates and times are prone to errors too. Handle them incorrectly, and they can confuse end-users and fellow programmers alike.

This is a quick guide to dealing with dates and times specifically in the PHP programming language. It’s meant to be a reference to the most common needs you’ll have, like formatting and adjusting dates. It’s simple, but it’s likely going to cover 80% of your needs.

Table of contents

This research is brought to you by support from Frontend Masters, CSS-Tricks’ official learning partner.

Need front-end development training?

Frontend Masters is the best place to get it. They have courses on all the most important front-end technologies. Interested in going full-stack? Here’s your best bet:


Get the current date and time

One thing to know is that the dates and times can be represented in three forms: a timestamp (i.e. epoch time), a DateTime object, and a string.

First up, a recipe to get the current date and time:

<?php  $  now = new DateTime(); var_dump($  now);  // object(DateTime)#1 (3) { //   ["date"]=> //   string(26) "2021-10-13 22:25:11.790490" //   ["timezone_type"]=> //   int(3) //   ["timezone"]=> //   string(12) "Asia/Jakarta" // }

This provides a DateTime object that can be used to create a date and time string:

<?php  $  now = new DateTime(); echo $  now->format("Y-m-d"); // 2021-10-13 echo $  now->format("Y-m-d h:i:s A"); // 2021-10-13 10:10:31 PM

Intuitively, you know that Y refers to the year, m refers to the month, d refers to the day of the month, and so on. The full list of the parameters can be found in the PHP manual, but I’ll drop some of the most common ones here for reference.

Day of the month
d Day of the month. two digits with leading zeros 01 – 31
j Day of the month without leading zeros 1 – 31
S Includes the English suffix. stndrdth (e.g. 1st2nd3rd4th)
Weekday
D Abbreviated textual representation of a day, in three letters Sun – Sat
l A full textual representation of a weekday. Sunday – Saturday
Month
F A full textual representation of a month, such as January or March January – December
M Abbreviated textual representation of a month, in three letters Jan – Dec
m Numeric representation of a month, with leading zeros 01 – 12
n Numeric representation of a month, without leading zeros 1 – 12
Year
Y A full numeric representation of a year, 4 digits E.g. 1999 or 2003
y A two digit representation of a year E.g. 99 or 03
Time
A Uppercase Ante Meridiem and Post Meridiem AM or PM
g 12-hour format of an hour without leading zeros 1 – 12
h 12-hour format of an hour with leading zeros 01 – 12
i Minutes with leading zeros 00 – 59
s Seconds with leading zeros 00 – 59

The DateTime object can be converted to a timestamp:

<?php  $  now = new DateTime(); echo $  now->getTimestamp(); // 1634139081

But we can also get the current time in timestamp without constructing a DateTime object:

<?php  echo time(); // 1634139081

Construct a DateTime object of a specific time

What if we want to construct a DateTime for a particular time, like July 14th, 2011? We can pass a formatted string date to the constructor:

<?php  $  date = new DateTime("2011-07-14"); var_dump($  date);  // object(DateTime)#1 (3) { //   ["date"]=> //   string(26) "2011-07-14 00:00:00.000000" //   ["timezone_type"]=> //   int(3) //   ["timezone"]=> //   string(12) "Asia/Jakarta" // }

The constructor accepts other formats as well:

<?php  $  date = new DateTime("14-07-2011"); var_dump($  date);  // object(DateTime)#1 (3) { //   ["date"]=> //   string(26) "2011-07-14 00:00:00.000000" //   ["timezone_type"]=> //   int(3) //   ["timezone"]=> //   string(12) "Asia/Jakarta" // }

But be careful with an ambiguous format, like this:

<?php  $  date = new DateTime("07/14/2011"); var_dump($  date);  // object(DateTime)#1 (3) { //   ["date"]=> //   string(26) "2011-07-14 00:00:00.000000" //   ["timezone_type"]=> //   int(3) //   ["timezone"]=> //   string(12) "Asia/Jakarta" // }

You might think that everyone should be familiar with an American date format. But not everyone is and it might be interpreted differently. Not PostgreSQL.

CREATE TABLE IF NOT EXISTS public.datetime_demo (   created_at date );  insert into datetime_demo (created_at) values ('07/12/2011');  select created_at from datetime_demo; /* 2011-12-07 */

You may have thought that would return July 12th, 2011, but it was December 7th, 2011, instead. A better way is to use an explicit format:

<?php  $  date = DateTime::createFromFormat('m/d/y', "10/08/21"); var_dump($  date);  //object(DateTime)#2 (3) { //  ["date"]=> //  string(26) "2021-10-08 16:00:47.000000" //  ["timezone_type"]=> //  int(3) //  ["timezone"]=> //  string(12) "Asia/Jakarta" //}

What if we want to construct a DateTime object from a timestamp?

<?php  $  date = new DateTime(); $  date->setTimestamp(1634142890); var_dump($  date);  //object(DateTime)#1 (3) { //  ["date"]=> //  string(26) "2021-10-13 23:34:50.000000" //  ["timezone_type"]=> //  int(3) //  ["timezone"]=> //  string(12) "Asia/Jakarta" // }

We don’t have to create a DateTime object if we want to convert a timestamp object to a formatted date string:

<?php  echo date("Y-m-d h:i A", time()); // 2021-10-14 04:10 PM

Timezones

We can create a DateTime object that includes timezone information, like if we’re dealing with Pacific Standard Time, Eastern Daylight Time, etc.

<?php  $  timezone = new DateTimeZone("America/New_York"); $  date = new DateTime("2021-10-13 05:00", $  timezone); var_dump($  date);  // object(DateTime)#1 (3) { //   ["date"]=> //   string(26) "2021-10-13 05:00:00.000000" //   ["timezone_type"]=> //   int(3) //   ["timezone"]=> //   string(16) "America/New_York" // }  // Eastern Daylight Time, for example: New York $  date = new DateTime("2021-10-13 05:00 EDT"); var_dump($  date);  // object(DateTime)#2 (3) { //   ["date"]=> //   string(26) "2021-10-13 05:00:00.000000" //   ["timezone_type"]=> //   int(2) //   ["timezone"]=> //   string(3) "EDT" // }  $  date = new DateTime("2021-10-13 05:00 -04:00"); var_dump($  date);  // object(DateTime)#1 (3) { //   ["date"]=> //   string(26) "2021-10-13 05:00:00.000000" //   ["timezone_type"]=> //   int(1) //   ["timezone"]=> //   string(6) "-04:00" // }

There are three ways to create a DateTime object with timezone information. The timezone_type accepts different values for each one.

But say we want to convert a date and time that’s displayed in New York’s timezone to display Jakarta’s timezone instead?

<?php  $  newYorkTimeZone = new DateTimeZone("America/New_York"); $  date = new DateTime("2021-11-11 05:00", $  newYorkTimeZone); echo $  date->format("Y-m-d h:i A"); // 2021-11-11 05:00 AM $  jakartaTimeZone = new DateTimeZone("Asia/Jakarta"); $  date->setTimeZone($  jakartaTimeZone); echo $  date->format("Y-m-d h:i A"); // 2021-11-11 05:00 PM

When it’s 05:00 AM in New York, it’s 05:00 PM in Jakarta on the same day. Jakarta is 12 hours ahead of New York on November 11th 2021. But one month earlier, Jakarta is only 11 hours ahead of New York as shown below:

<?php  $  newYorkTimeZone = new DateTimeZone("America/New_York"); $  date = new DateTime("2021-10-11 05:00", $  newYorkTimeZone); echo $  date->format("Y-m-d h:i A"); // 2021-10-11 05:00 AM $  jakartaTimeZone = new DateTimeZone("Asia/Jakarta"); $  date->setTimeZone($  jakartaTimeZone); echo $  date->format("Y-m-d h:i A"); // 2021-10-11 04:00 PM

PHP handles Daylight Saving Time for you automatically.

Localization

This is a common way to display date and time in the United States:

<?php  $  now = new DateTime(); echo $  now->format("m/d/Y h:i A"); // 10/14/2021 03:00 PM

But someone in France might prefer something more common to their locale. C’est horrible, they’d complain. For one, nobody puts the month before month day, except the U.S. Second, France doesn’t use AM or PM — they use the 24-hour format (e.g. 14:00 instead of 2:00 PM) like the military. This is how you make a French local happy.

<?php  $  now = new DateTime(); echo $  now->format("d/m/Y H:i"); // 14/10/2021 15:00

But this requires an intimate knowledge about a specific country or area. Instead, we can localize the date. To localize a date in PHP, first check for support for a particular language.

In this example, we’re using the French. In Ubuntu, install the French language pack:

$   sudo apt-get install language-pack-fr

Use the strftime() function to localize a date:

<?php  setlocale(LC_TIME, "en_US.UTF-8"); // Check for U.S. English support echo strftime("%x %X"); // 10/14/21 03:37:59 PM setlocale(LC_TIME, "fr_FR.UTF-8"); // Check for French support echo strftime("%x %X"); // 14/10/2021 15:37:31

All of the parameters for strftime are documented in the PHP manual.

Time travel

Let’s do time travel to the past and the future. First, let’s get acquainted with DateInterval:

<?php  $  interval = new DateInterval("P4M1W2DT2H5M");  // P 4M 1W 2D T 2H 5M // // P = Period interval (years, months, weeks, days) // 4M = 4 months // 1W = 1 week // 2D = 2 days // // T = Time interval (hours, minutes, seconds) // 2H = 2 hours // 5M = 5 minutes

The P and T are to separate period interval and time interval. Here’s how we can travel to the future:

<?php  $  date = new DateTime("2021-10-14"); $  interval = new DateInterval("P2D"); // 2 days $  futureDate = $  date->add($  interval); echo $  futureDate->format("Y-m-d"); // 2021-10-16

And here’s how we go back in time:

<?php  $  date = new DateTime("2021-10-14 10:00"); $  interval = new DateInterval("PT6H"); // 6 hours $  pastDate = $  date->sub($  interval); echo $  pastDate->format("Y-m-d H:i"); // 2021-10-14 04:00

If we want to time travel with the name of the weekday, we can combine the strtotime() function and the setTimestamp() method of a DateTime object:

<?php  $  nextTuesday = strtotime("next tuesday"); $  date = new DateTime("2021-10-14"); $  date->setTimestamp($  nextTuesday); echo $  date->format("Y-m-d"); // 2021-10-19

See the full list of strtotime() parameters in the PHP docs.

Recurring dates and times

It’s a common feature in calendar apps to set a reminder that repeats every so often, like every two days or every week. We can use DatePeriod to represent a period of time:

<?php  $  start = new DateTime("2021-10-01"); $  end = new DateTime("2021-11-01"); $  interval = new DateInterval("P1W"); // 1 week $  range = new DatePeriod($  start, $  interval, $  end);  // Starting from October 1st 2021 (inclusive), jump every 1 week // until November 1st 2021 (exclusive) foreach ($  range as $  date) {   echo $  date->format("Y-m-d") . "n"; }  // 2022-10-01 // 2022-10-08 // 2022-10-15 // 2022-10-22 // 2022-10-29

How many days ago?

You know how services like Twitter will show that someone posted X number of minutes/hours/days/etc. ago? We can do the same thing by calculating how much time has elapsed between the the current time and when that action occurred.

<?php  $  date = new DateTime("2022-10-30");  $  date2 = new DateTime("2022-10-25"); $  date3 = new DateTime("2022-10-10"); $  date4 = new DateTime("2022-03-30"); $  date5 = new DateTime("2020-03-30");  function get_period_ago($  endDate, $  startDate) {   $  dateInterval = $  endDate->diff($  startDate);    if ($  dateInterval->invert==1) {     if ($  dateInterval->y > 0) {       return $  dateInterval->y . " years agon";     } if ($  dateInterval->m > 0) {       return $  dateInterval->m . " months agon";     } if ($  dateInterval->d > 7) {       return (int)($  dateInterval->d / 7) . " weeks agon";     } if ($  dateInterval->d > 0) {       return $  dateInterval->d . " days agon";     }   } }  echo get_period_ago($  date, $  date2); // 5 days ago echo get_period_ago($  date, $  date3); // 2 weeks ago echo get_period_ago($  date, $  date4); // 7 months ago echo get_period_ago($  date, $  date5); // 2 years ago

After getting the DateInterval object from the diff() method, make sure that the $ startDate variable is in the past by checking the invert property. Then check the y, m, and d properties.

The full list of DateInterval object properties can be found here in the PHP docs.

Where do you go from here?

Now you have a little cheatsheet of common PHP recipes for when you find yourself working with dates and times. Need to get the current date and time? Maybe you need to format a date a certain way, or include the local timezone, or compare dates. All of that is right here!

There are still more methods and functions about date and time that we haven’t discussed, of course — things like calendar-related functions and whatnot. Be sure to keep the PHP Manual’s Date and Time section close by for even more use cases and examples.


The post PHP Date and Time Recipes appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

“Just in Time” CSS

I believe acss.io is the first usage of “Atomic CSS” where the point of it is to be a compiler. You write CSS like this:

<div class="C(#fff) P(20px)">   text </div>

And it will generate CSS like:

.C(#333) {   color: #333; } .P(20px) {   padding: 20px; }

(Or something like that.)

The point is that it only generates the CSS that you actually need, because you asked for it, and no more. The result is far less CSS than you’d see in an average stylesheet.

That complication process is what has come to be known as “Just in Time” CSS.

The popular Tailwind framework supports it. It kind of flips the mental model of Tailwind on its head, to me. Rather than providing a huge pile of CSS utility classes to use — then “purging” what is unused — it only creates what it needs to begin with.

I’d say “Just in Time” is a concept that is catching on. I just saw Assembler CSS and it leans into it big time. Rather than classes, you do stuff like:

<div x-style="grid; gap:1rem; grid-rows:1; grid-cols:1; sm|grid-cols:3">   <button x-style="^button:red">Submit</button> </div>

I’m pretty torn on this stuff. Some part of me likes how you can get styling done without ever leaving your templates. And I especially like the extremely minimal CSS output since CSS is a blocking resource. Another part of me doesn’t like that it’s a limited abstraction of CSS itself, so you’re at the mercy of the tool to support things that CSS can do natively. It also makes HTML a bit harder to look at — although I certainly got over that with JSX inline event handlers and such.


The post “Just in Time” CSS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Of Course We Can Make a CSS-Only Clock That Tells the Current Time!

Let’s build a fully functioning and settable “analog” clock with CSS custom properties and the calc() function. Then we’ll convert it into a “digital” clock as well. All this with no JavaScript!

Here’s a quick look at the clocks we’ll make:

Brushing up on the calc() function

CSS preprocessors teased us forever with the ability to calculate numerical CSS values. The problem with pre-processors is that they lack knowledge of the context after the CSS code has compiled. This is why it’s impossible to say you want your element width to be 100% of the container minus 50 pixels. This produces an error in a preprocessor:

width: 100% - 50px; // error: Incompatible units: 'px' and '%'

Preprocessors, as their name suggests, preprocess your instructions, but their output is still just plain old CSS which is why they can’t reconcile different units in your arithmetic operations. Ana has gone into great detail on the conflicts between Sass and CSS features.

The good news is that native CSS calculations are not only possible, but we can even combine different units, like pixels and percentages with the calc() function:

width: calc(100% - 50px);

calc() can be used anywhere a length, frequency, angle, time, percentage, number, or integer is allowed. Check out the CSS Guide to CSS Functions for a complete overview.

What makes this even more powerful is the fact that you can combine calc() with CSS custom properties—something preprocessors are unable to handle.

The hands of the clock

Image of a round clock with a light grey background. The hours hand is dark blue pointed at 2, the minutes are light blue pointed at 9 and the seconds are yellow and pointed at 2.

Let’s lay out the foundations first with a few custom properties and the animation definition for the hands of the analog clock:

:root {   --second: 1s;   --minute: calc(var(--second) * 60);   --hour: calc(var(--minute) * 60); } @keyframes rotate {   from { transform: rotate(0); }   to { transform: rotate(1turn); } }

Everything starts in the root context with the --second custom property where we defined that a second should be, well, one second (1s). All future values and timings will be derived from this.

This property is essentially the heart of our clock and controls how fast or slow all of the clock’s hands go. Setting --second to 1s makes the clock match real-life time but we could make it go at half speed by setting it to 2s, or even 100 times faster by setting it to 10ms.

The first property we are calculating is the --minute hand, which we want equal to 60 times one second. We can reference the value from the --second property and multiply it by 60 with the help of calc() :

--minute: calc(var(--second) * 60);

The --hour hand property is defined using the exact same principle but multiplied by the --minute hand value:

--hour: calc(var(--minute) * 60);

We want all three hands on the clock to rotate from 0 to 360 degrees—around the shape of the clock face! The difference between the three animations is how long it takes each to go all the way around. Instead of using 360deg as our full-circle value, we can use the perfectly valid CSS value of 1turn.

@keyframes rotate {   from { transform: rotate(0); }   to { transform: rotate(1turn); } }

These @keyframes simply tell the browser to turn the element around once during the animation. We have defined an animation named rotate and it is now ready to be assigned to the clock’s second hand:

.second.hand {   animation: rotate steps(60) var(--minute) infinite; }

We’re using the animation shorthand property to define the details of the animation. We added the name of the animation (rotate), how long we want the animation to run (var(--minute), or 60 seconds) and how many times to run it (infinite, meaning it never stops running). steps(60) is the animation timing function which tells the browser to perform the 1-turn animation in 60 equal steps. This way, the seconds hand ticks at each second rather than rotating smoothly along the circle.

While we are discussing CSS animations, we can define an animation delay (animation-delay) if we want the animation to start later, and we can change whether the animation should play forwards or backwards using animation-direction. We can even pause and restart the animations with animation-play-state.

The animation on the minute and hour hands will work very much like on the second hand. The difference is that multiple steps are unnecessary here—these hands can rotate in a smooth, linear fashion.

The minute hand takes one hour to complete one full turn, so:

.minute.hand {   animation: rotate linear var(--hour) infinite; }

On the other hand (pun intended) the hour hand takes twelve hours to go around the clock. We don’t have a separate custom property for this amount of time, like --half-day, so we will multiply --hour by twelve:

.hour.hand {   animation: rotate linear calc(var(--hour) * 12) infinite; }

You probably get the idea of how the hands of the clock work by now. But it would not be a complete example if we didn’t actually build the clock.

The clock face

So far, we’ve only looked at the CSS aspect of the clock. We also need some HTML for all that to work. Here’s what I’m using:

<main>   <div class="clock">     <div class="second hand"></div>     <div class="minute hand"></div>     <div class="hour hand"></div>   </div> </main>

Let’s see what we have to do to style our clock:

.clock {   width: 300px;   height: 300px;   border-radius: 50%;   background-color: var(--grey);   margin: 0 auto;   position: relative; }

We made the clock 300px tall and wide, made the background color grey (using a custom property, --grey, we can define later) and turned it into a circle with a 50% border radius.

There are three hands on the clock. Let’s first move these to the center of the clock face with absolute positioning:

.hand {   position: absolute;   left: 50%;   top: 50%; }

Notice the name of this class (.hands) because all three hands use it for their base styles. That’s important because any changes to this class are applied to all three hands.

Let’s define the hand dimensions and color things up:

.hand {   position: absolute;   left: 50%;   top: 50%;   width: 10px;   height: 150px;   background-color: var(--blue); }

The hands are now all in place:

A round clock with just one light blue hand pointing directly at 6.

Getting proper rotation

Let’s hold off celebrating just a moment. We have a few issues and they might not be obvious when the clock hands are this thin. What if we change them to be 100px wide:

A round clock with a light grey background. The light blue hand is thick and still pointed at 6, but

We can now see that if an element is positioned 50% from the left, it is aligned to the center of the parent element—but that’s not exactly what we need. To fix this, the left coordinate needs to be 50%, minus half the width of the hand, which is 50px in our case:

Grey circle with a red vertical line bisecting it in the center. Two red arrows are on the clock, one labeled 50% pointing at the red line and another labeled negative 50 pixels pointing away from the red line.

Working with multiple different measurements is a breeze for the calc() function:

.hand {   position: absolute;   left: calc(50% - 50px);   top: 50%;   width: 100px;   height: 150px;   background-color: var(--grey); }

This fixes our initial positioning, however, if we try to rotate the element we can see that the transform origin, which is the pivot point of the rotation, is at the center of the element:

Grey circle showing a blue rectangle on top rotated counter-clockwise.

We can use the transform-origin property to change the rotation origin point to be at the center of the x-axis and at the top on the y-axis:

.hand {   position: absolute;   left: calc(50% - 50px);   top: 50%;   width: 100px;   height: 150px;   background-color: var(--grey);   transform-origin: center 0; }

This is great, but not perfect because our pixel values for the clock hands are hardcoded. What if we want our hands to have different widths and heights, and scale with the actual size of the clock? Yes, you guessed right: we need a few CSS custom properties!

.second {   --width: 5px;   --height: 140px;   --color: var(--yellow); } .minute {   --width: 10px;   --height: 90px;   --color: var(--blue); } .hour {   --width: 10px;   --height: 50px;   --color: var(--dark-blue); }

With this, we’ve defined custom properties for the individual hands. What’s interesting here is that we gave these properties the same names: --width, --height, and --color. How is it possible that we gave them different values but they don’t overwrite each other? And which value will I get back if I call var(--width), var(--height) or var(--color)?

Let’s look at the hour hand:

<div class="hour hand"></div>

We assigned new custom properties to the .hour class and they are locally scoped to the element, which includes the element and all its children. This means any CSS style applied to the element—or its children accessing the custom properties—will see and use the specific values that were set within their own scope. So if you call var(--width) inside the hour hand element or any ancestor elements inside that, the value returned from our example above is 10px. This also means that if we try using any of these properties outside these elements—for example inside the body element—they are inaccessible to those elements outside the scope.

Moving on to the hands for seconds and minutes, we enter a different scope. That means the custom properties can be redefined with different values.

.second {   --width: 5px;   --height: 140px;   --color: var(--yellow); } .minute {   --width: 10px;   --height: 90px;   --color: var(--blue); } .hour {   --width: 10px;   --height: 50px;   --color: var(--dark-blue); } .hand {   position: absolute;   top: 50%;   left: calc(50% - var(--width) / 2);   width: var(--width);   height: var(--height);   background-color: var(--color);   transform-origin: center 0; }

The great thing about this is that the .hand class (which we assigned to all three hand elements) can reference these properties for the calculations and declarations. And each hand will receive its own properties from its own scope. In a way, we’re personalizing the .hand class for each element to avoid unnecessary repetition in our code.

Our clock is up and running and all hands are moving at the correct speed:

The finished clock. Grey background, with minutes and hours pointing to 6 and seconds pointing to 7.

We could stop here but let me suggest a few improvements. The hands on the clock start at 6 o’clock, but we could set their initial positions to 12 o’clock by rotating the clock 180 degrees. Let’s add this line to the .clock class:

.clock {   /* same as before */   transform: rotate(180deg); }

The hands might look nice with rounded edges:

.hand {   /* same as before */   border-radius: calc(var(--width) / 2); }

Our clock looks and works great! And all hands start from 12 o’clock, exactly how we want it!

Setting the clock

Even with all these awesome features, the clock is unusable as it fails terribly at telling the actual time. However, there are some hard limitations to what we can do about this. It’s simply not possible to access the local time with HTML and CSS to automatically set our clock. But we can prepare it for manual setup.

We can set the clock to start at a certain hour and minute and if we run the HTML at exactly that time it will keep the time accurately afterwards. This is basically how you set a real-world clock, so I think this is an acceptable solution. 😅

Let’s add the time we want to start the clock as custom properties inside the .clock class:

.clock {   --setTimeHour: 16;   --setTimeMinute: 20;   /* same as before */ }

The current time for me as I write is coming up to 16:20 (or 4:20) so the clock will be set to that time. All I need to do is refresh the page at 16:20 and it will keep the time accurately.

OK, but… how can we set the time to these positions and rotate the hands if a CSS animation is already controlling the rotation?

Ideally, we want to rotate and set the hands of the clock to a specific position when the animation starts at the very beginning. Say you want to set the hour hand to 90 degrees so it starts at 3:00 pm and initialize the rotation animation from this position:

/* this will not work */ .hour.hand {   transform: rotate(90deg);   animation: rotate linear var(--hour) infinite; }

Well, unfortunately, this will not work because the transform is immediately overridden by the animation, as it modifies the very same transform property. So, no matter what we set this to, it will go back to 0 degrees where the first keyframe of the animation starts.

We can set the rotation of the hand independently from the animation. For example, we could wrap the hand into an extra element. This extra parent element, the “setter,” would be responsible for setting the initial position, then the hand element inside could animate from 0 to 360 degrees independently. The starting 0-degree position would then be relative to what we set the parent setter element to.

This would work but luckily there’s a better option! Let me amaze you! 🪄✨✨

The animation-delay property is what we usually use to start the animation with some predefined delay.

The trick is to use a negative value, which starts the animation immediately, but from a specific point in the animation timeline!

To start 10 seconds into the animation:

animation-delay: -10s;

In case of the hour and minute hands, the actual value we need for the delay is calculated from the --setTimeHour and --setTimeMinute properties. To help with the calculation, let’s create two new properties with the amount of time shifting we need:

  • The hour hand needs to be the hour we want to set the clock, multiplied by an hour.
  • The minute hand shifts the minute we want to set the clock multiplied by a minute.
--setTimeHour: 16; --setTimeMinute: 20; --timeShiftHour: calc(var(--setTimeHour) * var(--hour)); --timeShiftMinute: calc(var(--setTimeMinute) * var(--minute));

These new properties contain the exact amount of time we need for the animation-delay property to set our clock. Let’s add these to our animation definitions:

.second.hand {   animation: rotate steps(60) var(--minute) infinite; } .minute.hand {   animation: rotate linear var(--hour) infinite;   animation-delay: calc(var(--timeShiftMinute) * -1); } .hour.hand {   animation: rotate linear calc(var(--hour) * 12) infinite;   animation-delay: calc(var(--timeShiftHour) * -1); }

Notice how we multiplied these values by -1 to convert them to a negative number.

We have almost reached perfection with this, but there’s a slight issue: if we set the number of minutes to 30, for example, the hour hand needs to already be halfway through to the next hour. An even worse situation would be to set the minutes to 59 and the hour hand is still at the beginning of the hour.

fix this, all we need to do is add the minute shift and the hour shift values together for the hour hand:

.hour.hand {   animation: rotate linear calc(var(--hour) * 12) infinite;   animation-delay: calc(     (var(--timeShiftHour) + var(--timeShiftMinute)) * -1   ); }

And I think with this we have fixed everything. Let’s admire our beautiful, pure CSS, settable, analog clock:

Let’s go digital

In principle, an analog and a digital clock both use the same calculations, the difference being the visual representation of the calculations.

Clock with a rectangular light grey background with the numeric time reading 14 45 18 in 24-hour format.

Here’s my idea: we can create a digital clock by setting up tall, vertical columns of numbers and animate these instead of rotating the clock hands. Removing the overflow mask from the final version of the clock container reveals the trick:

The new HTML markup needs to contain all the numbers for all three sections of the clock from 00 to 59 on the second and minute sections and 00 to 23 on the hour section:

<main>   <div class="clock">     <div class="hour section">       <ul>         <li>00</li>         <li>01</li>         <!-- etc. -->         <li>22</li>         <li>23</li>       </ul>     </div>     <div class="minute section">       <ul>         <li>00</li>         <li>01</li>         <!-- etc. -->         <li>58</li>         <li>59</li>       </ul>     </div>     <div class="second section">       <ul>         <li>00</li>         <li>01</li>         <!-- etc. -->         <li>58</li>         <li>59</li>       </ul>     </div>   </div>  </main>

To make these numbers line up, we need to write some CSS, but to get started with the styling we can copy over the custom properties from the :root scope of the analog clock straight away, as time is universal:

:root {   --second: 1s;   --minute: calc(var(--second) * 60);   --hour: calc(var(--minute) * 60); }

The outermost wrapper element, the .clock, still has the very same custom properties for setting the initial time. All that’s changed is that it becomes a flexbox container:

.clock {   --setTimeHour: 14;   --setTimeMinute: 01;   --timeShiftHour: calc(var(--setTimeHour) * var(--hour));   --timeShiftMinute: calc(var(--setTimeMinute) * var(--minute));    width: 150px;   height: 50px;   background-color: var(--grey);   margin: 0 auto;   position: relative;   display: flex; }

The three unordered lists and the list items inside them that hold the numbers don’t need any special treatment. The numbers will stack on top of each other if their horizontal space is limited. Let’s just make sure that there is no list styling to prevent bullet points and that we center things for consistent placement:

.section > ul {   list-style: none;   margin: 0;   padding: 0; } .section > ul > li {   width: 50px;   height: 50px;   font-size: 32px;   text-align: center;   padding-top: 2px; }

The layout is done!

Outside each unordered list is a <div> with a .section class. This is the element that wraps each section, so we can use it as a mask to hide the numbers that fall outside the visible area of the clock:

.section {   position: relative;   width: calc(100% / 3);   overflow: hidden; }

The structure of the clock is done and the rails are now ready to be animated.

Animating the digital clock

The basic idea behind the whole animation is that the three strips of numbers can be moved up and down within their masks to show different numbers from 0 to 59 for seconds and minutes, and 0 to 23 for hours (for a 24-hour format).

We can do this by changing the translateY transition function in CSS for the individual strips of numbers from 0 to -100%. This is because 100% on the y-axis represents the height of the whole strip. A value of 0% will show the first number, and 100% will show the last number of the current strip.

Previously, our animation was based on rotating a hand from 0 to 360 degrees. We now have a different animation that moves the number strips from 0 to -100% on the y-axis:

@keyframes tick {   from { transform: translateY(0); }   to { transform: translateY(-100%); } }

Applying this animation to the seconds number strip can be done the same way as the analog clock. The only difference is the selector and the name of the animation that’s referenced:

.second > ul {   animation: tick steps(60) var(--minute) infinite; }

With the step(60) setting, the animation ticks between numbers like we did for the second hand on the analog clock. We could change this to ease and then the numbers would smoothly slide up and down as if they were on a ribbon of paper.

Assigning the new tick animation to the minute and hour sections is just as straightforward:

.minute > ul {   animation: tick steps(60) var(--hour) infinite;   animation-delay: calc(var(--timeShiftMinute) * -1); } .hour > ul {   animation: tick steps(24) calc(24 * var(--hour)) infinite;   animation-delay: calc(var(--timeShiftHour) * -1); }

Again, the declarations are very similar, what’s different this time is the selector, the timing function, and the animation name.

The clock now ticks and keeps the correct time:

Time reading 14 1 10 in 24-hour format.

One more detail: The blinking colon (:)

Again we could stop here and call it a day. But there’s one last thing we can do to make our digital clock a little more realistic: make the colon separator between the minutes and seconds blink as each second passes.

We could add these colons in the HTML but they are not part of the content. We want them to be an enhancement to the appearance and style of the clock, so CSS is the right place to store this content. That’s what the content property is for and we can use it on the ::after pseudo-elements for the minutes and hours:

.minute::after, .hour::after {   content: ":";   margin-left: 2px;   position: absolute;   top: 6px;   left: 44px;   font-size: 24px; }

That was easy! But how can we make the seconds colon blink too? We want it animated so we need to define a new animation? There are many ways to achieve this but I thought we should change the content property this time to demonstrate that, quite unexpectedly, it is possible to change its value during an animation:

@keyframes blink {   from { content: ":"; }   to { content: ""; } }

Animating the content property is not going to work in every browser, so you could just change that to opacity or visibility as a safe option…

The final step is to assign the blink animation to the pseudo-element of the minute section:

.minute::after {   animation: blink var(--second) infinite; }

And with that, we are all done! The digital clock keeps the time accurately and we even managed to add a blinking separator between the numbers.

Book: All you need is HTML and CSS

This is just one example project from my new book, All you need is HTML and CSS. It’s available on Amazon in both the U.S and U.K.

If you are just getting started with web development, the ideas in the book will help you level up and build interactive, animated web interfaces without touching any JavaScript.

If you are a seasoned JavaScript developer, the book is a good reminder that many things can be built with HTML and CSS alone, especially now that we have a lot more powerful CSS tools and features, like the ones we covered in this article. There are many examples in the book pushing the limits including interactive carousels, accordions, calculating, counting, advanced input validation, state management, dismissible modal windows, and reacting to mouse and keyboard inputs. There’s even a fully working star rating widget and a shopping basket.

Thanks for spending the time to build clocks with me!


The post Of Course We Can Make a CSS-Only Clock That Tells the Current Time! appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Exploring color-contrast() for the first time.

I saw in the release notes for Safari Technical Preview 122 that it has support for a color-contrast() function in CSS. Safari is first out of the gate here. As far as I know, no other browser supports this yet and I have no idea when stable Safari will ship it, or if any other browser ever will. But! It’s a very good idea! Any native tool to get us to ship more accessible interfaces (of which color contrast is a part) is cool by me. So let’s attempt to get it to work.

Anybody can download Safari Technical Preview, so I did that.

I had to ask around about this, but just because this is a pre-release browser, it doesn’t mean all these features are active by default. Just as Chrome Canary has feature flags you have to turn on, so does Safari Technical Preview. So, I had to flip it on like this:

The release notes don’t have any information about how to actually use color-contrast(), but fortunately web searching turns up a spec (it’s part of Color Module 5), and MDN has a page for it with very basic syntax information.

This is how I understand it:

That example above is a little silly, because it will always return white — that has the most contrast with black. This function actually gets useful when one or more of those color values is dynamic, meaning very likely it is a CSS custom property.

The function returns a color, so the top use-case, I would guess, is going to be setting a color based on a dynamic background. So…

section {   background: var(--bg);   color: color-contrast(var(--bg), white, black); }

Now we can toss any color at all at --bg and we’ll either get white or black text, depending on what has the most contrast:

That’s extremely cool, even in the most basic use case.

Here’s a demo from Dave where he’s not just setting the text color in the parent, but the color of links as well, and the links have a different set of colors to choose from. Play with the HSL sliders (in Safari Technology Preview, of course) to see it work.

Just picking two colors that have enough contrast is easy enough (although you’d be surprised how often it’s screwed up by even those of us with good intentions). But oh wow does it get complicated quick with different situations, let alone having a bunch of color variations, or god forbid, arbitrary combinations.

Here’s a video of me playing with Dave’s tester so you can see how the colors update at different places.


The post Exploring color-contrast() for the first time. appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Splitting Time Between Product and Engineering Efforts

At each company I’ve worked, we have had a split between time spent on Product initiatives and Engineering work. The percentages always changed, sometimes 70% Product, 30% Engineering, sometimes as much as a 50/50 split. The impetus is to make sure that Engineering spends a portion of their time building new features, but also ensures we can do “our own” work such as address technical debt, upgrade systems, and document our code.

The trouble is, it’s one thing to say this at the outset, and another to make it a reality. There are some reasons that I’ve seen this model fail, not because people don’t understand in theory that it’s valuable, but because in practice, there are some common pitfalls that you have to think through. We’ll cover some of these scenarios from the perspective of an Engineering leader so that we can address a good path forward.

The issues

Some of the pointers below are reactions and planning based on what can go wrong, so let’s talk first about the challenges you can encounter if this isn’t set up right.

  • Product may have conflicts, either with the work itself or the time involved. This can strain the relationship between Product and Engineering. If they are caught by surprise, you can potentially find the boundaries of your work getting more restrictive.
  • Your engineers might not understand what’s expected of them. Parallelization of efforts can be hard to do, so building a good process can provide clarity.
  • Maintenance path should be clear: Are you planning on making a giant system upgrade? This may affect other teams over time and if you’re not clear about eventual ownership, it could come back to haunt you. 👻

As nice as it is to have some freedom in your engineering time, communication, planning, and clear expectations can help make sure that you avoid any of the issues outlined above.

A group of people working
Photo by Akson on Unsplash

Communication

Once you figure out what problem you would like to tackle, it’s critical to write up a small one-sheeter that you can share with stakeholders on the nature of the work, the amount of time it’s going to take, and why it’s important.

If it’s a large project, you can also scope those pieces down into GitHub/GitLab/Jira issues, and add a label for the type of work that it is. This is great because you can use whatever project management system you already use to elevate the amount of work and expectations weekly. It’s good to keep the dialogue open with your Product partners on scope and the nature of the work so they aren’t surprised by other work getting done. This will largely vary by the culture of the team and organization.

This can help provide clarity for your Engineers, too. If they understand the nature of the work and what’s expected of them, it’s easier for them to tackle the small issues that make up a whole.

You may find that it makes less sense from a focus perspective to have every engineer split time across product and engineering projects. They may instead prefer to split the work up between themselves: three people on product work for a few weeks, one person on engineering work. There are also times where everyone does need to be involved so that they have equal institutional knowledge (migrations can be like this, depending on what it is). Your mileage may vary based on the size of the team, the amount of product work, and the type of project. 

Communication helps here, too — if you’re not sure what the right path is, it can help to have a small brainstorm as a group on how you want to get this done. Just be sure you also align everyone with why the project is important as you do so.

Types of projects

There are many types of projects that you can create in your Engineering team time, and each has slightly different approaches from what I’ve seen, so let’s go over each one of them.

Tech debt

Let’s address technical debt first because that’s one of the most common pieces of work that can unlock your team. For every feature you write, if Engineering effort is slowed, you’re not only losing time in terms of product development, you’re also losing money in terms of engineering time in salary.

A bit of technical debt is natural, particularly at smaller companies where it makes more fiscal sense to move quickly, but there are some points where tech debt becomes crippling for development and releases, and makes a codebase unstable. Sometimes it needs to be done immediately to make sure all your engineers can work efficiently, and sometimes it’s gradual.

In a lot of cases, the technical debt pieces are things you learn you need by a bottoms-up approach: the devs that are closest to working with the system will know best what day-to-day technical debt exists than Engineering Managers (EMs) typically will. The challenge as an EM is to notice larger patterns, like when many folks complain of the same thing, rather than one dev who may have a strong opinion. Asking around before you start this type of project can help — poll people on how much time they think they’re wasting in a given week vs the prospect of an alternative.

Sometimes technical debt is a matter of a large amount of refactor. I’ve seen this go best when people are up front on what kind of pull requests (PR) are necessary. Do you need to update the CSS in a million spots? Or convert old class components to hooks? You probably don’t want one huge PR for all of it, but it doesn’t make sense to break this work per-component either. Work together as a team on how much each PR will hold and what is expected of the review so you don’t create a “review hole” while the work is being done.

Two people looking at some code
Photo by heylagostechie on Unsplash

Innovative projects

A lot of companies will do hack week/innovation week projects where devs can work on some feature related to the company’s product untethered. It’s a great time for exploration, and I’ve seen some powerful features added to well-known applications this way. It’s also incredibly energizing for the team to see an idea of their own come to fruition.

The trouble with doing these kinds of projects in the split engineering time is that you can, at times, make the Product team feel a little slighted. Why? Well, think of things from their perspective. Their job is to put forth these features, plan carefully with stakeholders, put together roadmaps (often based on company metrics and research), and get on the Engineering schedule, usually working with a project manager. If you spend half your time working on unplanned features, you can potentially fork an existing plan for a project, go against some of the known research they have, or simply slow down the process to get a core make-it-or-break it feature they need.

The way I’ve seen this play out well is when the EM communicates up front with Product. Consider this a partnership: if Product says that a particular feature doesn’t make sense, they likely have a good reason for thinking so. If you can both hear each other out, there is likely a path forward where you both agree. 

It’s good to address their fears, too. Are they concerned that there won’t be enough time for product work? Ask your team directly how many weeks at half time they think it might take (with the expectations that things might shift once they dig in). Make it clear to everyone that you don’t expect it to be done at a break-neck pace.

Ultimately, communication is key. Ideally, these are small projects that won’t derail anything that can be done in parallel to the regular work. My suggestion is to try it with something very small first to see what bumps in the road there might be, and also build trust with Product that you’ll still get your work done and not “go rogue.”

The final piece of this is to figure out who is responsible for metrics, outcomes, and when things don’t go well. Part of the reason Product gets to decide direction is because they’re on the hook when it fails. Make sure you’re clear that as an Engineering leader, you’re taking responsibility for outcomes, both the good and the bad to maintain a good relationship.

Slow, ongoing work

This is probably the most clear-cut of any of the types of projects and will likely get the least amount of pushback from anyone. Examples of this type of work is internal documentation, tooling (if you don’t have a dedicated tools team), or small bits of maintenance.

The communication needed here is a little different from other projects, as it’s not necessarily going to be one constrained project that you ship, but rather an iterative process. Take documentation as an example: I would suggest building time for internal documentation into any feature process. 

For instance, let’s say you created a new feature that allows teams to collaborate. Not everyone across the company may know that you created a microservice for this feature that any team can use, and what parameters are expected, or how to add functionality down the road. Internal docs can be the difference between the service being used, as well as your team being asked to pair with someone every time someone needs to use it. Or worse: them trying to hack around and figure it out on their own, creating a mess of something that could have been worked on quicker and more efficiently.

Unlike the innovation projects, slow, ongoing work is typically not something folks really crave doing, so setting a process and expectations up straightaway works best. Internal documentation is a sometimes hidden but very important part of a well-functioning team. It helps with onboarding, getting everyone on the same page about system architecture, and can even help devs really solidify what they built and think through how they’re solving it.

Two women talking about something on a computer screen
Photo by Christina @ wocintechchat.com on Unsplash

Migrations

Migrations are handled a little differently than some other types of projects because it likely affects everyone. There is no one right way to do this, and will also largely depend on what type of migration it is — framework to framework, breaking down a monolith, and migrating to a different build process or server all may have different approaches. Due to the fact that each one of these is likely an article of its own, let’s go through some high level options that apply to the organization of them.

  • My first suggestion is to do as much research as possible up front on whatever type of migration you’re doing. There’s no way to know everything, but you don’t want to get part-way through a process to find out something critical. This is also helpful information to share with stakeholders.
  • Are there internal debates about what direction your company should head in? Timebox a unit of time to work through the problem and make sure you have a clear decision-maker at the end. A lot of tech problems don’t have one “true” solution, so having one owner make the decision and everyone else disagree and commit can help. But you also want to give a moment for folks to have their voices heard about what gives them pause, even if they are in disagreement — they might be thinking of something you’re not.
  • Document a migration plan, both at a high level and then work through the impact on each team. This is also a great time to explain to Product why this work is important: is your codebase becoming old and can no longer play well with other libraries and tools? Did a new build process come out that could save your engineers time in a release process? Help them understand why the work is critical.
  • Be clear about maintenance and ownership. If one team migrates a build process that then causes issues for another, who’s fixing things to unblock that team? You should decide this before it happens.
  • Some migration paths allow you do things slowly over time, or team by team, or do a lot of the work up front. However, there is usually a time when it’s going to be critical and all hands on deck are needed. Unlike some of the other work that can be parallelized, you may have to work something out with Product where all other feature work is stalled for a little bit while you get the new system in place. If you work closely with them, you may find that there are times in the season where you naturally have more of a customer lull, and it could give you the breathing room you need to get this done. I’d suggest that if they’re willing to let you take Engineering time to 100% for a little while, you return the favor; and once the platform is stable, dedicate 100% of the team’s time to Product work.

Celebrate!

This final step might seem optional, but it’s a big deal in my opinion. Your team just pulled off something incredible: they parallelized efforts, they were good partners to Product, they got something done for the Engineering org at large. It’s crucial to celebrate the work like you would a launch.

The team needs to know you value this work because it’s often thankless, but very impactful. It can also build trust to know that if something hairy comes up in the future, that it does actually help their career path as well. Celebrating with your team what you accomplished costs very little, and has great cultural impacts.


The post Splitting Time Between Product and Engineering Efforts appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

Time for Next-Gen Codecs to Dethrone JPEG

AVIF has been getting a lot of tech press, but Jon Sneyers is hot on JPEG XL (which makes sense as he’s the “chair of the JPEG XL ad hoc group in the JPEG Committee”). According to Jon’s comparison, JPEG XL comes out on top on everything, except low fidelity compression, and offers progressive rendering which none of the other next-gen codecs do. But WebP (not to be confused with the upcoming WebP2!) has something of a leg up now that it has support across all the major browsers.

There is a whole ecosystem around image formats that is way wider than websites, of course, and I’m sure that plays a big role in what ends up on websites. What format do you get when you make screenshots on your system? What does your digital camera export? What does your favorite design software export? Then, once people have images, does the website-making software you use support them? I think of how WordPress rejects SVG unless you force it; I just tried uploading an AVIF for this post and it won’t take that, either.

I also think of the UX of new formats, like when I have a .avif file on my desktop, my macOS computer doesn’t know what to make of it. It’s just a blank white document with no preview. The image ecosystem as a whole moves slower than the web. Inertia, as Jon puts it, is a good framing, but hopefully can be overcome:

Let’s just hope that the new codecs will win the battle, which is mostly one that’s against inertia and the “ease” of the status quo. Ultimately, unless JPEG remains a dominant force, regardless of which new codec will prevail, we’ll reap the benefits of stronger compression, higher image fidelity, and color accuracy, which translate to more captivating, faster-loading images.

I’d bet that image codecs evolve as long as displaying images on screens is a thing. There is no endgame. The blog post I’m linking to from Jon is on the Cloudinary blog, and I gotta give it to them: Cloudinary — and services like it — are a solution here. They provide a system where I don’t have to care about image formats all that much. I upload whatever I have (ideally: big and high-quality) and they can serve the best possible format, size, and quality for the situation. That job, to me, is just too damn hard to do manually, let alone stay on top of long-term.


I see JPEG 2000 is still hanging out, but whatever happened to JPEG XR? It wasn’t that long ago we talked about serving that, even with <source>. Was that just mostly an IE thing that died with IE?

Direct Link to ArticlePermalink


The post Time for Next-Gen Codecs to Dethrone JPEG appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

PSA: It’s That Time to Update the Copyright Year on Your Site

Every year about this time I see articles going around reminding people how to update the copyright on their websites. Usually somewhere in the footer. You know, a line like:

© Copyright 2007-2019 CSS-Tricks

I am very absolutely not a lawyer, but this is how I understand it:

  • You don’t actually need that if your goal is copyrighting blog posts. Blog posts are copyrighted (in the United States) the second you publish them, with or without a copyright notice. You just can’t sue anybody over infringement unless you register the copyright.
  • People say it may “defer” infringements (but I don’t buy it).
  • People say it may win you greater settlements should you sue and win (but I wouldn’t even know where to begin fact-checking that).

Personally, I usually don’t bother with it, but don’t take that advice. I feel like it’s usually included for a bit of swagger like, “lookie how long we’ve been around.” In that same tune, if you’re doing it, it makes a lot of sense to keep it up to date because having the incorrect or an outdated date definitely makes your site look stale.

So, sure, rock your <?php echo date("Y"); ?> or whatever you need to do to keep it up to date. Just be careful: I just saw a site going around that recommended an inline JavaScript document.write() technique. That’s probably not the worst thing in the world since it’s just injecting a string, but it’s usually something to avoid for various reasons, and I’d way rather see you do it server-side or pre-rendered.

The post PSA: It’s That Time to Update the Copyright Year on Your Site appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Save Big on An Event Apart for a Limited Time!

(This is a sponsored post.)

If you could get one gift from your boss this holiday season, what would you want it to be? You know, other than the usual mouse pad, picture frame or, my favorite, the ol’ coffee mug and Starbucks card combo.

What if you were to receive something, hmm, more substantial? Like something that keeps giving three days instead of one. Or something that levels up your front-end chops. Or something that lets you network with your peers and gain invaluable experience by learning from the brightest minds in the biz?

Yes, all of that would be awesome — nay — epic! And, yes, you can get all of that together with one ticket to An Event Apart.

There truly is no better gift for front-enders. It’s a break from the desk into a three-day haven of learning about the most important movements in web development and design from the folks who are leading the way. Seriously, just check out the lineup for the Washington D.C. installment.

This is where we’d normally share a special coupon code that scores you a $ 100 discount and gives us credit for sending you there. You can totally do that by entering AEACP at checkout. We rely on sponsorships around here and supporting our sponsors keeps this boat moving.

But! An Event Apart are feeling extra generous this season and offering a discount of $ 200 off any two- or three-day pass to any of their 2020 events. That includes stops in D.C., Seattle, Boston, Minneapolis, Orlando and San Francisco!

OK, so what’s that code? You’re just gonna have to watch out on Twitter, Facebook or their mailing list on Friday, December 13, to get it.

Learn More

Direct Link to ArticlePermalink

The post Save Big on An Event Apart for a Limited Time! appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Save Big on An Event Apart for a Limited Time!

(This is a sponsored post.)

If you could get one gift from your boss this holiday season, what would you want it to be? You know, other than the usual mouse pad, picture frame or, my favorite, the ol’ coffee mug and Starbucks card combo.

What if you were to receive something, hmm, more substantial? Like something that keeps giving three days instead of one. Or something that levels up your front-end chops. Or something that lets you network with your peers and gain invaluable experience by learning from the brightest minds in the biz?

Yes, all of that would be awesome — nay — epic! And, yes, you can get all of that together with one ticket to An Event Apart.

There truly is no better gift for front-enders. It’s a break from the desk into a three-day haven of learning about the most important movements in web development and design from the folks who are leading the way. Seriously, just check out the lineup for the Washington D.C. installment.

This is where we’d normally share a special coupon code that scores you a $ 100 discount and gives us credit for sending you there. You can totally do that by entering AEACP at checkout. We rely on sponsorships around here and supporting our sponsors keeps this boat moving.

But! An Event Apart are feeling extra generous this season and offering a discount of $ 200 off any two- or three-day pass to any of their 2020 events. That includes stops in D.C., Seattle, Boston, Minneapolis, Orlando and San Francisco!

OK, so what’s that code? You’re just gonna have to watch out on Twitter, Facebook or their mailing list on Friday, December 13, to get it.

Learn More

Direct Link to ArticlePermalink

The post Save Big on An Event Apart for a Limited Time! appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]