Front End Web Development

What’s New With Forms in 2022?

Css Tricks - Thu, 09/08/2022 - 3:12am

Browsers are constantly adding new HTML, JavaScript and CSS features. Here are some useful additions to working with forms that you might have missed…

requestSubmit()

Safari 16 will be the final browser to add support for requestSubmit.

Before we look at how .requestSubmit() works, let’s remind ourselves how programmatically submitting a form with JavaScript works when using the .submit() method. Submitting a form with submit() does not trigger a submit event. So in the following code, the form is submitted, preventDefault() has no effect, and nothing is logged to the console:

const form = document.forms[0]; form.addEventListener('submit', function(event) { // code to submit the form goes here event.preventDefault(); console.log('form submitted!'); }); document.querySelector('.btn').addEventListener('click', function() { form.submit(); })

.submit() will also ignore any HTML form validation. Given the following markup, the form will be submitted when the input is empty even though the input has a required attribute:

<form> <label for="name">Name</label> <input required name="name" id="name" type="text"> </form>

.requestSubmit() is an alternative way to submit a form using JavaScript, but in contrast to .submit(), HTML form validation will prevent the form from being submitted. If all the data entered in the form passes validation, the submit event will be fired, meaning “form submitted!” would be logged to the console in the following example:

form.addEventListener('submit', function(event) { event.preventDefault(); console.log('form submitted!'); }); document.querySelector('.btn').addEventListener('click', function() { form.requestSubmit(); })

You could already achieve this by programmatically clicking the form’s submit button, but requestSubmit is perhaps a more elegant solution.

submitter property of submit event

The SubmitEvent.submitter property gained full cross-browser support with the release of Safari 15.4. This read-only property specifies the <button> or <input type="submit"> element that caused a form to be submitted.

<form> <button name="foo" value="bar" type="submit">Bar</button> <button name="foo" value="baz" type="submit">Baz</button> </form>

When you have multiple submit buttons or inputs, each with a different value, only the value of the button or input that was clicked on to submit the form will be sent to the server, rather than both values. That’s nothing new. What is new is that the event listener for the submit event now has access to the event.submitter property. You can use this to add a class to the button or input that triggered the form submission, for example, or to obtain its value or any other of its HTML attributes.

document.forms[0].addEventListener('submit', function(event) { event.preventDefault(); console.log(event.submitter.value); console.log(event.submitter.formaction); event.submitter.classList.add('spinner-animation'); }) formdata event

This isn’t particularly new, but only achieved cross-browser support with the release of Safari 15. The main use case for the formdata event is enabling custom elements to take part in form submissions. Outside of web components, though, it can still be useful.

You add a formdata event listener to the form you want to interact with:

document.querySelector('form').addEventListener('formdata', handleFormdata);

The event is fired both by a regular HTML form submission and also by an occurrence of new FormData(). event.formData holds all of the data being submitted.

function handleFormdata(event) { for (const entry of event.formData.values()) { console.log(entry); } }

The callback function for the formdata event listener runs before the data is sent to the server, giving you a chance to add to or modify the data being sent.

function handleFormdata(event) { event.formData.append('name', 'John'); }

You could have modified or appended the FormData inside the submit event handler but formdata allows you to separate out the logic. It’s also an alternative to using hidden inputs in the markup of your form in cases where you are submitting the form “the old fashioned way” — i.e. relying on the built-in functionality of HTML to submit the form rather than doing it with fetch.

showPicker() for input elements

showPicker() has been supported since Chrome 99, Firefox 101, and in the upcoming Safari 16. For an input element whose type attribute is either Date, Month, Week, Time, datetime-local, color, or file, showPicker() provides a programmatic way to display the selection UI. For color and file inputs, it’s always been possible to programmatically show the picker by calling .click on the input:

document.querySelector('input[type="color"]').click();

That approach doesn’t work on date inputs, which is why this new API was added. .showPicker() will also work with color and file inputs but there’s no real advantage to using it over .click().

Inert attribute

It’s always been possible to disable multiple inputs at once by wrapping them in a HTML fieldset and disabling the fieldset:

CodePen Embed Fallback

Inert is a new HTML attribute. It isn’t only for forms, but forms are certainly a key use-case. Unlike the disabled attribute, inert can be applied to a form element itself. Everything within the form will be non-focusable and non-clickable. When it comes to assistive technologies, inert is similar to setting aria-hidden="true". Unlike the disabled attribute, inert does not apply any styling by default, but it’s easy to add your own:

form[inert] { opacity: .2; } CodePen Embed Fallback There’s more to come…

The big one is styling <select> elements, something developers have wanted for decades. It looks set to finally become a reality sometime soon with the introduction of selectmenu.

But that’s it for now! The recent updates bring full browser support to form features we’ve been waiting for, making them prime for production use.

What’s New With Forms in 2022? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Overlapping Bar Charts

Css Tricks - Wed, 09/07/2022 - 3:46am

As the name suggests, overlapping charts visualize two different sets of data in a single diagram. The idea is that the overlapping bars allow us to compare data, say, year-over-year. They are also useful for things like tracking progress for a goal where one bar represents the goal and the other shows the current amount.

But they’re beautiful too!

Your mind is probably like mine and is already starting to figure out how you’d go off and code that. Here’s how I tackled it.

CodePen Embed Fallback The HTML

We’re going to start with markup because, well, that’s how we know what needs styling.

<div class="container"> <div class="chart"> <dl class="numbers"> <dd><span>100%</span></dd> <!-- all the way to 0% --> </dl> <dl class="bars"> <div> <dt>2018</dt> <dd> <div class="bar" data-percentage="50"></div> <div class="bar overlap" data-percentage="53"></div> </dd> </div> <div> <!-- more bars --> </dl> </div> </div>

We will be using description lists (<dl>) as it is a much more semantic approach as compared to standard ordered and unordered lists. Another reason is that we are including a label within each bar. Normal lists do not have a tag within them to add a title or description unlike definition lists. In simple terms, it just makes more sense and is more readable too.

The first description list, .numbers, is the y-axis. The .bars is where the data is visualized and I’ve made a definition list to build the x-axis as well. Each list item contains a .bar and the label as a description term (dt).

And what’s up with the data attribute? The data-percentage is being used to specify the height of the bar, which ultimately represents its value on the y-axis. We could manually set it in CSS for each bar, but that is repetitive and a lot of extra code that can be replaced with a few lines of CSS.

The basic chart styles

We’re working with a lot of two-dimensional directions, so flexbox is going to be our friend for getting everything lined up. We can make the .chart element a flexible container that positions the y-axis labels and the chart beside one another in the row direction.

.chart { display: flex; }

We don’t even need to specify the direction since flexbox defaults to row. Let’s do that and then add flexbox to the list of labels along the y-axis while we’re at it since we know those will run in the column direction.

.numbers { display: flex; flex-direction: column; list-style: none; margin: 0 15px 0 0; padding: 0; } CodePen Embed Fallback

Believe it or not, we can use flexbox again for the bars since, they too, are running in a row direction.

.bars { display: flex; flex: auto; /* fill up the rest of the `.chart` space */ gap: 60px; }

I’ve set this up so that the .bars automatically take up whatever space is leftover by the y-axis .numbers.

You probably noticed it in the HTML, but “bar” is actually two bars where one overlaps the other. I wrapped those in a generic <div> that we can use as yet another flexible container that holds the definition term (<dt>) we’re using as a label and the description details (<dd>) that holds both bar values:

.bars > div { align-items: center; display: flex; flex-direction: column; flex: 1; position: relative; }

Each bar is going to be the same width, hence flex: 1. We’re relatively positioning the element while we’re at it because we’re about to absolutely position each bar and we want to make sure they stay in their containers.

CodePen Embed Fallback

Each bar has a percentage height that corresponds to the values along the vertical y-axis. You may also remember that we gave each bar a data-percentage attribute — we’re going to sprinkle in a little JavaScript that sets the height of each bar using those values.

var bars = document.querySelectorAll("dd .bar"); bars.forEach((bar) => { var height = bar.getAttribute("data-percentage"); bar.style.height = height + "%"; });

That’s our basic chart!

CodePen Embed Fallback

We want to get this to where we can see the bars overlapping one another. That’s next!

Overlapping bars

The trick to get one bar to overlap another is funny because we’re often trying to prevent things from overlapping visually in CSS. But in this case, we actually want that to happen.

The bars are already overlapping; it’s just tough to tell. Notice in the HTML that the second .bar in each set has an additional .overlap class. Let’s use that to differentiate the bars. You’re totally free to choose your own styling for this. I’m adding a little padding to the .overlap bars so that they are wider than the other bars. Then I’m tweaking the stacking order using z-index so that the .overlap bars sit below the other bars.

CodePen Embed Fallback Let’s add a legend

Legend. Such a great word, isn’t it? Packed with all kinds of meaning. In this case, it’s a more than a nice touch because, visually, we’re jamming two bars in spaces that are typically reserved for one bar. A legend provides context that explains what each bar represents.

<figure class="legend"> <div class="type1">Estimate</div> <div class="type2">Actual</div> </figure>

Using a <figure> feels correct to me. They’re often used to wrap images, but the spec says they’re used “to annotate illustrations, diagrams, photos, code listings, etc.” and we’re working with a diagram. We could probably use an unordered list to hold the items, but I went with an unsemantic <div>. If anyone has an opinion on the best way to mark this up, I’m all ears in the comments!

Once again, styling is totally up to you:

CodePen Embed Fallback Accessibility considerations

We’ve spent a bunch of our effort on making decisions for the markup and styling of our overlapping bar chart. It’s great so far, but we’re definitely not done because there’s more we can do to make this a more accessible experience. Not everyone is a sighted web surfer, so there’s some additional work to do to convey the content in those contexts.

Specifically, we need to:

  1. check that our colors have plenty of contrast between them,
  2. allow keyboard users to tab to each overlapping bar, and
  3. make sure screen readers announce the content.
Color contrasts

We need enough contrast between:

  • the overlapping bars
  • the bars and the chart background
  • the label text and background

I did a little homework in advance on the colors I used in the examples we’ve look at so far, making sure that there is enough contrast between the foregrounds and backgrounds to achieve WCAG AA compliance.

Here’s what I’m using:

  • Overlapping bars: (#25DEAA and #696969: 3.16:1 ratio)
  • Bars and chart background (#696969 and #111: 3.43:1 ratio)
  • Y-axis label text and background (#fff and #333: 12.63: 1 ratio)
Tabbing between bars

To get this where keyboard users can select each individual bar with the Tab key, we can reach for the HTML tabindex attribute. We can use the following JavaScript inside the for-each function to add this property to each bar (both of them). We will set the tab index to 0:

bar.setAttribute("tabindex", 0);

We can also add some CSS to improve the outline when the bar is selected while we’re at it:

.bar:focus { outline: 1.5px solid #f1f1f1; } Announcing content on screen readers

Another important aspect of accessibility is making sure screen readers can announce the bars and their percentages.

We’re working with two different charts in one: a chart that shows “Estimated” values and another that shows “Actual” values. It’d be great if the user knew which bar was being announced, so let’s label them with the aria-label attribute:

<div class="bar" data-percentage="50" aria-label="Estimate">50%</div>

Notice that we have the bar’s value directly in the HTML as well. That will get announced, but we still want to visually hide it. We could use transparent text for that, but another way is to use the classic .visually-hidden trick by wrapping the value in span:

<div class="bar" data-percentage="50" aria-label="Estimate"> <span class="visually-hidden">50%</span> </div> .visually-hidden { clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }

While we’re talking about announcing content, we can probably prevent the y-axis labels from being read. It’s not like the user is missing information, as the actual percentages for each bar are already available and announced. We can use the aria-hidden attribute for that:

<dl class="numbers" aria-hidden="true"> <dd><span>100%</span></dd> <dd><span>80%</span></dd> <dd><span>60%</span></dd> <dd><span>40%</span></dd> <dd><span>20%</span></dd> <dd><span>0%</span></dd> </dl>

I also think it’s OK for screen readers to ignore the legend since it’s a visual aid:

<figure class="legend" aria-hidden="true"> <div class="type1">Estimate</div> <div class="type2">Actual</div> </figure> The final demo CodePen Embed Fallback That’s a wrap!

There we go, a chart with overlapping bars! It’s a nice way to compare data and I hope you can find a use for it on some project.

Are there other ways we could have approached this? Of course! Everything we covered here is merely walking you through my thought process. I imagine some of you would have taken a different approach — if that’s you, please share! It’d be great to see other CSS layout techniques and perspectives on nailing the accessibility.

Overlapping Bar Charts originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Steven Heller’s Font of the Month: Farandole & Lustik

Typography - Tue, 09/06/2022 - 9:33pm

Read the book, Typographic Firsts

This month it's Font of the Month with an s! Yes, Steve heller takes a look at two unorthodox layer font families from rising French star Francis Chouquet. Vive la play!

The post Steven Heller’s Font of the Month: Farandole & Lustik appeared first on I Love Typography.

Hacking CSS Animation State and Playback Time

Css Tricks - Tue, 09/06/2022 - 8:00am

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.

CodePen Embed Fallback

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:

CodePen Embed Fallback

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; } CodePen Embed Fallback

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; } CodePen Embed Fallback

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:

CodePen Embed Fallback

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:

CodePen Embed Fallback

… or does it?

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

CodePen Embed Fallback

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”:

CodePen Embed Fallback

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:

CodePen Embed Fallback

… 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); } } CodePen Embed Fallback

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:

CodePen Embed Fallback

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:

CodePen Embed Fallback

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:

CodePen Embed Fallback

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.

CodePen Embed Fallback

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:

CodePen Embed Fallback

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.

Flutter For Front-End Web Developers

Css Tricks - Mon, 09/05/2022 - 10:10am

I started as a front-end web developer and then became a Flutter developer. I think there were some concepts that helped me adopt Flutter easier. There were also some new concepts that were different.

In this article, I want to share my experience and inspire anyone feeling paralyzed with choosing one ecosystem over the other by showing how concepts transfer over and any new concepts are learnable.

Concepts That Transferred Over

This section shows places where front-end web development and Flutter resemble. It explains skills that you already have that are an advantage to you if you start Flutter.

1. Implementing User Interfaces (UIs)

To implement a given UI in front-end web, you compose HTML elements and style them with CSS. To implement UIs in Flutter, you compose widgets and style them with properties.

Like CSS, the Color class in Dart works with “rgba” and “hex”. Also like CSS, Flutter uses pixels for space and size units.

In Flutter, we have Dart classes and enums for almost all CSS properties and their values. For example:

Flutter also has Column and Row widgets. These are the Flutter equivalent for display: flex in CSS. To configure justify-content and align-items styles, you use MainAxisAlignment and CrossAxisAlignment properties. To adjust the flex-grow style, wrap the affected child(ren) widget(s) of the Column/Row, in an Expanded or Flexible.

For the advanced UIs, Flutter has the CustomPaint class – it is to Flutter what the Canvas API is to web development. CustomPaint gives you a painter to draw any UI as you wish. You will usually use CustomPaint when you want something that is really complex. Also, CustomPaint is the go-to way when a combination of widgets doesn’t work.

2. Developing For Multiple Screen Resolutions

Websites run on browsers and mobile apps run on devices. As such, while developing for either platform, you have to keep the platform in mind. Each platform implements the same features (camera, location, notifications, etc.) in different ways.

As a web developer, you think about your website’s responsiveness. You use media queries to handle what your website looks like in smaller and wider screens.

Coming over from mobile web development to Flutter, you have the MediaQuery helper class. The MediaQuery class gives you the current device orientation (landscape or portrait). It also gives you the current viewport size, the devicePixelRatio, among other device info. Together, these values give you insights about the mobile device’s configuration. You can use them to change what your mobile app looks like at various screen sizes.

3. Working with Debuggers, Editors, and Command Line Tools

Desktop browsers have developer tools. These tools include an inspector, a console, a network monitor, etc. These tools improve the web development process. Flutter too has its own DevTools. It has its widget inspector, debugger, network monitor, among other features.

IDE support is also similar. Visual Studio Code is one of the most popular IDE for web development. There are many web-related extensions for VS Code. Flutter too supports VS Code. So while transitioning, you don’t need to change IDE. There are Dart and Flutter extensions for VS Code. Flutter also works well with Android Studio. Both Android Studio and VS Code support Flutter DevTools. These IDE integrations make Flutter tooling complete.

Most front-end JavaScript frameworks come with their command-line interface (CLI). For example: Angular CLI, Create React App, Vue CLI, etc. Flutter also comes with an exclusive CLI. The Flutter CLI permits you to build, create, and develop Angular projects. It has commands for analyzing and testing Flutter projects.

Concepts That Were New

This section talks about Flutter-specific concepts that are easier or non-existent in web development. It explains ideas you should keep in mind as you enter Flutter.

How To Handle Scrolling

When developing for the web, default scrolling behavior is handled by web browsers. Yet, you are free to customize scrolling with CSS (using overflow).

This is not the case in Flutter. Widget groups don’t scroll by default. When you sense that widget groups might overflow, you have to proactively configure scrolling.

In Flutter, you configure scrolling by using peculiar widgets that permit scrolling. For example: ListView, SingleChildScrollView, CustomScrollView, etc. These scrollable widgets give you great control over scrolling. With CustomScrollView, you can configure expert and complex scroll mechanisms within the application.

On Flutter’s side, using ScrollViews is inevitable. Android and iOS have ScrollView and UIScrollView to handle scrolling. Flutter needs a way to unify the rendering and developer experience by using its ScrollViews, too.

It may help to stop thinking about the flow of document structure and instead consider the application as an open canvas for a device’s native painting mechanisms.

2. Setting Up Your Development Environment

To create the simplest website, you can create a file with a .html extension and open it in a browser. Flutter is not that simple. To use Flutter, you need to have installed the Flutter SDK and have configured Flutter for a test device. So if you want to code Flutter for Android, you need to set up the Android SDK. You will also need to configure at least one Android device (an Android Emulator or a physical device).

This is the same case for Apple devices (iOS and macOS). After installing Flutter on a Mac, you still need to set up Xcode before going further. You will also need at least an iOS simulator or an iPhone to test Flutter on iOS. Flutter for desktop is also a considerable setup. On Windows, you need to set up the Windows Development SDK with Visual Studio (not VS Code). On Linux, you will install more packages.

Without extra setup, Flutter works on browsers. As a result, you could overlook the extra setup for target devices. In most cases, you would use Flutter for mobile app development. Hence, you would want to setup at least Android or iOS. Flutter comes with the flutter doctor command. This command reports the status of your development setup. That way, you know what to work on, in the setup, if need be.

This doesn’t mean that development in Flutter is slow. Flutter comes with a powerful engine. The flutter run command permits hot-reloading to the test device while coding. But then you will need to press R to actually hot-reload. This is not an issue. Flutter’s VS Code extension permits auto-hot-reload on file changes.

3. Packaging and Deployment

Deploying websites is cheaper and easier compared to deploying mobile applications. When you deploy websites, you access them through domain names. These domain names are usually renewed annually. However, they are optional.

Today, many platforms offer free hosting.

For example: DigitalOcean gives you a free subdomain on .ondigitalocean.com.

You can use these domains if you are building a documentation website. You can also use them when you are not worried about branding.

In Flutter’s world with mobile applications, you usually in most cases deploy your app to two places.

You have to register a developer account on each of these platforms. Registering a developer account requires a fee or subscription and identity verification.

For App Store, you need to enroll for the Apple Developer program. You need to maintain an annual subscription of $99. For Google Play, you need to make a one-time $25 payment for the account.

These stores review every app reviewed before it goes live.

Also bear in mind that users don’t easily consume app updates. Users must explicitly update installed applications. This is in contrast to the web where everyone just gets to see the latest deployed version of a website.

Managing deployed applications is relatively more demanding than managing deployed websites. However, this shouldn’t scare you. After all, there are millions of apps deployed on either stores so you can add yours, too.

Additional Thoughts On Flutter

Flutter is a cross-platform tool to build desktop, mobile, or web applications. Flutter apps are pixel-perfect. Flutter paints the same UI on each app irrespective of the target platform. This is because each Flutter app contains the Flutter engine. This engine renders the Flutter UI code. Flutter provides a canvas for each device and allows you to paint as you want. The engine communicates with the target platform to handle events and interactions.

Flutter is efficient. It has high-performance levels. This is because it is built with Dart and it leverages Dart’s features.

With these many benefits, Flutter is a good choice for many applications. Cross-platform apps save money and time during production and maintenance. However, Flutter (and cross-platform solutions) might not be an optimum choice in some cases.

Don’t use Flutter if you want users to use platform developer tools with your application. Platform developer tools here mean device-specific tools like Android developer options. It also includes browser developer tools.

Don’t use Flutter for web if you expect browser extensions to interact with the website.

Also, don’t use Flutter for content-heavy websites.

Conclusion

This was a review of skills that carry over from front-end web development to working with Flutter. We also discussed app development concepts that you have to learn as a web developer.

Flutter is simpler for web developers because they both involve implementing UIs. If you start Flutter, you will find out that it gives you a good developer experience. Give Flutter a try! Use it to build mobile apps and of course, showcase what you build.

Cheers!

Flutter For Front-End Web Developers originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Behind the CSScenes, September 2022

Css Tricks - Thu, 09/01/2022 - 10:06am

Those of you who have been reading CSS-Tricks for a while may remember that we used to publish a little thing we called CSS-Tricks Chronicles. Our friend Chris Coyier would write up a reflection from the past couple of months or so, and it was a great way to get a pulse on what’s happening around CSS-Tricks, the site, and what the team is doing.

We like that and want to keep it going. It’s a new era, though! So what we’re going to do is welcome you back to what we’re now calling Behind the CSScenes. You’re going to meet some new and familiar faces in these updates, starting with Haley Mills, who is kicking off the very first issue.

How’s the transition going?

[Haley Mills:] Before we dive in, let me start by introducing myself! My name is Haley, and I’m the manager of Content Integration here at DigitalOcean. I’ve been at DigitalOcean for 5 years and previously worked on our editorial team, helping authors publish all sorts of topics through our Write For DOnations program. 

Many folks here at DigitalOcean (including myself) are avid readers of CSS-Tricks, and we still have to pinch ourselves for how lucky we are to be entrusted with this community. We recognize that CSS-Tricks is a critical free resource for devs across the world, and my goal is to keep it that way. 

  • Since the acquisition, we have published 95 pieces of new content and look forward to growing that number.
  • In the month of August, we performed maintenance on 6 existing pieces of content.

That said, change is to be expected when passing a torch.

I think we all know that no one can replace Chris’ voice — it’s a big reason why CSS-Tricks is, well, CSS-Tricks. His ability to have you laughing while learning something new is a skill that few can compete with. I know many of you miss his writing because you told us so in a survey (which we’ll get to in a bit), but it also opens up a huge opportunity for us all to take the torch and continue doing what CSS-Tricks does best:

Find creative solutions to problems and share them with the world. Chris brought people together this way on CSS-Tricks — and you can give back, too.

Your blossoming idea could turn out to be what the Flexbox Guide is for me and so many other people, so I humbly encourage you to reach out in our Guest Writing Form and talk to us about your topic ideas. We have two awesome editors, Geoff and Brad, to help you shape and bring your ideas to life to share with the CSS-Tricks community. In addition to paying you for your contribution, we will now also make a matching donation to a tech-focused charity of your choice.

Next up, we have Product Manager Karen Degi with some survey result highlights.

The results are in…

[Karen Degi:] In June, we shared a survey to collect feedback to help shape the future of CSS-Tricks. We received almost 900 responses, including some great written responses that helped us understand what CSS-Tricks means to the larger community. 

Many of you also volunteered to talk to us directly, which has us thinking about the best way to gather those thoughts. If you’re one of those folks, know that we haven’t forgotten about you and still want to hear from you. We just want to make sure we approach this in the most effective way!

The survey confirmed some things we already suspected and brought new things to our attention. The top few things that grabbed our attention are:

  • Engaging, high-quality content is at the heart of CSS-Tricks. We’re working to make sure that we continue investing in in-depth guides on front-end topics, as well as providing short articles about quick tricks and tutorials with embedded demos.
  • You love RSS! As we continue investing in CSS-Tricks and bringing new functionality, we’ll make sure we keep an eye on how our changes affect the RSS feed.
  • You come to CSS-Tricks to learn, to be entertained, and to do your jobs better. You do not come to CSS-Tricks because you’re excited about being sold…well, anything, really. Although we think DigitalOcean is pretty great, and we’ll probably continue to talk about ourselves where it makes sense, we understand that we need to do so in a way that is honest, trustworthy, and connected to your needs as a front-end enthusiast.

Next up is Logan Liffick, Senior Digital Experience Designer, with redesign updates.

A redesign is in the works!

[Logan Liffick:] If you’ve worked on the front end — or really anywhere on the web, you’re bound to know CSS-Tricks. It’s where I, and many others, started the journey. So, when I was asked to spearhead a redesign for the site, it was nothing short of an honor. Without a doubt, undertaking a brand update for something so familiar to so many is a challenge of incredible magnitude

If I were to do justice to this project, I’d need to pay tribute to the original. That mentality became the underlying theme of my work, and any effort to rejuvenate took inspiration from existing patterns and styles from the site.

Upon first glance, you’ll notice the fresh coat of paint. Past that, you’ll recognize the site reads more “editorial” than before. This was a purposeful decision to accentuate existing type stylings and, more importantly, to pay homage to the essence of CSS-Tricks as an informational resource. 

Preserving the element of “fun” was also top of mind. Sprinkled throughout the site are various snippets from the actual CSS “tricks” shared on this site — for example, there’s going to be a little Easter egg tucked inside a sticky footer using Preethi’s slide-out effect that’s my personal favorite, a fantastic suggestion from Geoff himself. Gradients are now a core color-way in the system, and border-radii have been rounded out. 

We wanted to give ourselves permission and space to explore an open-ended and malleable system far into the future, which lines up nicely with the overall mission and goal of CSS-Tricks: to explore what’s possible with CSS. This is just the beginning, there’s so much more to see, do, and learn with CSS-Tricks living in our (digital) ocean.

Next is Geoff with author highlights!

New authors!

[Geoff:] We’ve added a few new faces to our growing list of guest authors who have contributed to CSS-Tricks:

You may have also seen our editor Bradley Kouchi’s name pop up a couple of times, and you can expect to continue seeing him on a semi-regular basis.

That’s 16 new authors! You can be one, too, by filling out our guest writing form.

On a related note, I’m pleased as punch that we still get regular contributions from a large band of familiar faces from before the DigitalOcean acquisition. Just look at all the fine folks who’ve continued to share their great ideas with us:

Big shake-ups like the one we’re going through today can be scary. Seeing these familiar names in article bylines has helped me a ton as far as continuity and consistency go. CSS-Tricks still seems very CSS-Tricks-y to me, and that’s a big deal.

Until next time…

We hope you’ve enjoyed this little peek behind the CSScenes! We’ll do it again… and again and again. As you can tell, there’s a lot of activity happening around here, which means we’ll have lots to share in the next edition.

Oh, and if you’re one of the many who’ve told us just how much you miss the newsletter, it’s still here! We’re sending it just once a month while we get back in the swing of things, and you may very well need to re-subscribe to get it (we had to do a lot of scrubbing after the keys to the site were handed over).

Thanks for reading!

Behind the CSScenes, September 2022 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

A Brief Introduction to JAWS, NVDA, and VoiceOver

Css Tricks - Thu, 09/01/2022 - 7:47am

A screen reader is an important accessibility tool for people with no or limited vision. People who are blind or those with low vision can use a screen reader to navigate the computer. Screen readers will read contents on the screen and explain to the user what is on the page. Screen readers allow people to use the computer for daily tasks.

There are many screen reader software available for people through their operating system or through open source projects.

A 2021 research by WebAim found that from 1568 responders, more than 53.7 percent of people surveyed used JAWS on Windows, more than 30.7 percent of people used NVDA on Windows and little over 6.5 percent of people used VoiceOver on macOS.

JAWS and NVDA for Windows and VoiceOver for macOS are the most popular screen readers people use.

First, I should clarify that this article will be written from my point of view. To give background, I have been a front-end developer at a non-profit for people with learning differences for over three years. I, along with my colleagues, seek to make our projects more accessible every day. I am not visually impaired and do not use these tools on a regular basis. For work, I have a Mac machine and test accessibility using VoiceOver.

Here is my planned testing methodology:

  1. Navigate the page by heading, until “Accessibility APIs” section.
  2. In the “Accessibility APIs” section, read the content and the unordered list inside.
  3. TAB to hear focusable items in the unordered list.
  4. Jump to the Search field.
  5. TAB to hear a few items in the navigation section

To find similarities and differences between them, I decided to test a set of steps with each screen reader on a Wikipedia page about screen readers. I will browse the web with Chrome for my tests. Testing all screen readers on the same page and browser will reduce the amount of variables and keep the tests consistent.

JAWS

JAWS is an acronym for Job Access With Speech and is the most widely used screen reader in the world. It is only available on Windows. Depending on the plan and features, JAWS can be purchased anywhere from $90 yearly license all the way to $1605 for perpetual license.

JAWS has predefined keyboard commands to navigate the web. Full list of keyboard commands can be found on their website.

Demonstration JAWS Demo

In the beginning of the demo, I am clicking on H key on my keyboard to go to the next heading. JAWS is moving down the page, reading me the headings along with their level.

Later in the video, I am clicking on number 2 and number 3 on my keyboard to have JAWS read Heading Levels 2s then later Heading Levels 3s. This is a great feature because we can move down the page and sections by heading level and get a better sense of the page layout.

When I reach the “Accessible APIs” section, I press the DOWN ARROW key until the third item in the unordered list.

Later in the demo, I am clicking on the TAB key for JAWS to read to me the next focusable item on the page, which is inside this list. I click TAB until I reach a focusable element in another section.

Then I press F key to focus on the search field, which JAWS reads to me.

Then I click on TAB and JAWS focuses on the navigation elements that are on the side of the page.

Pros & Cons

Pros:

  • JAWS is more customizable than other screen readers.
  • There are more options to navigate through the page.
  • JAWS is industry standard.
  • Widely used, which means there are lots of user to user support.

Cons:

  • JAWS is more complicated to use than NVDA or VoiceOver.
  • Some commands are not intuitive.
  • There are a lot more commands for the user to learn.
  • More learning curve for users.
  • JAWS is also not available on the Mac, which limits its users.
  • Costs anywhere between $90 – $1605 for the user.
  • JAWS has different key commands for desktop and laptop which may make it harder for users to transfer knowledge and may cause confusion.
NVDA

NVDA, or NonVisual Digital Access, is available on Windows only. Users need to download the software from NVDA’s website, NVAccess. This software is free to download but does not come already installed on Windows machines. NVDA is the second most popular screen reader in the world according to WebAim’s 2021 survey.

Like other screen readers, NVDA has defined keyboard commands to navigate the web. NVDA’s full keyboard commands can be found on their website.

Demonstration NVDA Demo

In the demo I am clicking on H key on the keyboard to go to the next heading. First, NVDA reads me Heading Level 1, which is “Screen reader”. Then NVDA goes to read Heading Level 2s and 3s.

When I reach “References” I begin to click on TAB on my keyboard for NVDA to focus on next focusable items.

After focusing on a few items on the list, I click ENTER and go to the New York Times page.

Pros & Cons

Pros:

  • Overall, I found NVDA was able to provide me with information on the screen.
  • The out-of-the-box keyboard commands were easy to use and easy to learn.
  • NVDA is open source, which means the community can update and fix.
  • NVDA is free, which makes it an affordable option to Windows users.

Cons:

  • NVDA is not available on the Mac, which limits its users.
VoiceOver

VoiceOver is the screen reader used in Mac. VoiceOver is only available on Mac not available in Windows. VoiceOver is free and is already installed on the computer, which removes barriers because this is part of the computer setup and the user does not have to download or purchase any additional software.

VoiceOver has defined keyboard commands to navigate the web. VoiceOver’s full keyboard commands can be found on their website.

Demonstration VoiceOver Demo

In the demo, I am on a Wikipedia page and I am clicking on the VoiceOver Command (which is Control+Option) along with Command+H to navigate through the headings. VoiceOver reads the headings in order, starting from Heading Level 1, “Screen Reader”, to Heading Level 2, “Contents”, to Heading Level 3, and so on.

When I reach the “Accessibility APIs” section, I click on VoiceOver Command plus the RIGHT ARROW, to tell VoiceOver that I want it to read this section. Later I am clicking on the VoiceOver Command plus the RIGHT ARROW on my keyboard, to navigate the section.

When I get on to the third item on the unordered list, I press TAB on my keyboard to focus on the next focusable element.

I press TAB a few times, then I press VoiceOver Command plus U, to open the Form Control Menu. In the menu, I press DOWN ARROW until I hear the “Search Wikipedia” option. When I hear it, I click ENTER and the screen reader focuses on the form field. In the form field, I press TAB to navigate to the navigation section.

Pros & Cons

Pros:

  • VoiceOver is easy to use and learn.
  • VoiceOver’s commands are intuitive.
  • Free tool that comes installed in every macOS device.

Cons:

  • VoiceOver is also not available on Windows, which limits its users.
  • VoiceOver is not an app and can only be updated when Apple releases macOS update.
Key Takeaways

A screen reader is an important accessibility tool for people with no or limited vision. Screen readers allow people to use the computer for daily tasks.

There are many screen reader softwares available. In this article I compared JAWS, NVDA, and VoiceOver.

Here is a comparison chart overview of the three screen readers:

JAWSNVDAVoiceOverOperating SystemWindowsWindowsmacOSPrice$90 – $1695FreeFree# of users50%30%6%Ease of Use (subjective)HardEasyEasy

I found that for basic screen reader testing, most screen readers follow a similar keystroke pattern and knowledge from one screen reader can be used for others.

All screen readers have their pros and cons. Ultimately, it’s up to user preference and also the operating system they use to determine which screen reader software is best for them.

Previously: “Small Tweaks That Can Make a Huge Impact on Your Website’s Accessibility” (2018), and “Why, How, and When to Use Semantic HTML and ARIA” (2019), “15 Things to Improve Your Website Accessibility” (2020), “5 Accessibility Quick Wins You Can Implement Today” (2022).

A Brief Introduction to JAWS, NVDA, and VoiceOver originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

iShadeed’s Container Queries Lab

Css Tricks - Thu, 09/01/2022 - 4:29am

Ahmad Shadeed got an early jump on container queries and has a growing collection of examples based on everyday patterns.

And, if you missed it, his latest post on container queries does a wonderful job covering how they work since landing in Chrome 105 this month (we’ll see them in Safari 16 soon). Some choice highlights and takeaways:

  • Containers are defined with the container-type property. Previous demos and proposals had been using contain instead.
  • Container queries are very much like the media queries we’ve been writing all along to target the viewport size. So, rather than something like @media (min-width: 600px) {}, we have @container (min-width: 600px) {}. That should make converting many of those media queries to container queries fairly straightfoward, minus the work of figuring out the new breakpoint values.
  • We can name containers to help distinguish them in our code (e.g. container-name: blockquote).

Great job, Ahmad! And thanks for sharing!

To Shared LinkPermalink on CSS-Tricks

iShadeed’s Container Queries Lab originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Interpolating Numeric CSS Variables

Css Tricks - Tue, 08/30/2022 - 3:17am

We can make variables in CSS pretty easily:

:root { --scale: 1; }

And we can declare them on any element:

.thing { transform: scale(var(--scale)); }

Even better for an example like this is applying the variable on a user interaction, say :hover:

:root { --scale: 1; } .thing { height: 100px; transform: scale(var(--scale)); width: 100px; } .thing:hover { --scale: 3; } CodePen Embed Fallback

But if we wanted to use that variable in an animation… nada.

:root { --scale: 1; } @keyframes scale { from { --scale: 0; } to { --scale: 3; } } /* Nope! */ .thing { animation: scale .25s ease-in; height: 100px; width: 100px; }

That’s because the variable is recognized as a string and what we need is a number that can be interpolated between two numeric values. That’s where we can call on @property to not only register the variable as a custom property, but define its syntax as a number:

@property --scale { syntax: "<number>"; initial-value: 1; inherits: true; }

Now we get the animation!

CodePen Embed Fallback

You’re going to want to check browser support since @property has only landed in Chrome (starting in version 85) as of this writing. And if you’re hoping to sniff it out with @supports, we’re currently out of luck because it doesn’t accept at-rules as values… yet. That will change once at-rule()becomes a real thing.

CodePen Embed Fallback

Interpolating Numeric CSS Variables originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Using Grid Named Areas to Visualize (and Reference) Your Layout

Css Tricks - Fri, 08/26/2022 - 3:44am

Whenever we build simple or complex layouts using CSS Grid, we’re usually positioning items with line numbers. Grid layouts contain grid lines that are automatically indexed with positive and negative line numbers (that is unless we explicitly name them). Positioning items with line numbers is a fine way to lay things out, though CSS Grid has numerous ways to accomplish the same with an undersized cognitive encumbrance. One of those ways is something I like to think of as the “ASCII” method.

The ASCII method in a nutshell

The method boils down to using grid-template-areas to position grid items using custom-named areas at the grid container level rather than line numbers.

When we declare an element as a grid container using display: grid, the grid container, by default, generates a single-column track and rows that sufficiently hold the grid items. The container’s child elements that participate in the grid layout are converted to grid items, irrespective of their display property.

For instance, let’s create a grid by explicitly defining columns and rows using the grid-template-columns and grid-template-rows properties.

.grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: repeat(3, 200px); }

This little snippet of CSS creates a 3×2 grid where the grid items take up equal space in the columns, and where the grid contains three rows with a track size of 200px.

We can define the entire layout with named grid areas using the grid-template-areas property. According to the spec, the initial value of grid-template-areas is none.

grid-template-areas = none | <string>+

<string>+ is listing the group of strings enclosed with a quote. Each string is represented as a cell, and each quoted string is represented as a row. Like this:

grid-template-areas: "head head" "nav main" "foot foot";

The value of grid-template-areas describes the layout as having four grid areas. They are,

  • head
  • nav
  • main
  • foot

head and foot span two column tracks and one row track. The remaining nav and main each span one column track and one row track. The value of grid-template-areas is a lot like arranging ASCII characters, and as Chris suggested a while back, we can get a visualization of the overall structure of the layout from the CSS itself which is the most trouble-free way to understand it.

(Full size GIF)

OK, so we created our layout with four named grid areas: head, nav, main, foot.

Now, let’s start to position the grid items against named grid areas instead of line numbers. Specifically, let’s place a header element into the named grid area head and specify the named grid area head in the header element using the grid-area property.

Named grid areas in a grid layout are called idents. So, what we just did was create a custom ident named head that we can use to place items into certain grid tracks.

header { grid-area: head; }

We can other HTML elements using other custom idents:

nav { grid-area: nav; } main { grid-area: main; } footer { grid-area: foot; } Writing named area values

According to CSS Grid Layout Module Level 1, all strings must be defined under the following tokens:

  • Named cell token: This represents the named grid area in the grid. For instance, head is a named cell token.
  • Null cell token: This represents the unnamed grid area in the grid container. For instance, an empty cell in the grid is a null cell token.
  • Trash token: This is a syntax error, such as an invalid declaration. For instance, a disparate number of cells and rows compared to the number of grid items would make a declaration invalid.

In grid-template-area, every quoted string (the rows) must have the same number of cells and define the complete grid without ignoring any cell.

We can ignore a cell or leave it as an empty cell using the full-stop character (.)

.grid { display: grid; grid-template-areas: "head head" "nav main" "foot ."; }

If that feels visually awkward or imbalanced to you, we can use multiple full-stop characters without any whitespaces separating them:

.grid { display: grid; grid-template-areas: "head head" "nav main" "foot ...."; }

A named cell token can span multiple grid cells, But those cells must form a rectangular layout. In other words, we’re unable to create “L” or “T”-shaped layouts, although the spec does hint at support for non-rectangular layouts with disconnected regions in the future.

ASCII is better than line-based placement

Line-based placement is where we use the grid-column and grid-row properties to position an element on the grid using grid line numbers that are automatically indexed by a number:

.grid-item { grid-column: 1 / 3; /* start at grid column line 1 and span to line 3 */ }

But grid item line numbers can change if our layout changes at a breakpoint. In those cases, it’s not like we can rely on the same line numbers we used at a specific breakpoint. This is where it takes extra cognitive encumbrance to understand the code.

That’s why I think an ASCII-based approach works best. We can redefine the layout for each breakpoint using grid-template-areas within the grid container, which offers a convenient visual for how the layout will look directly in the CSS — it’s like self-documented code!

.grid { grid-template-areas: "head head" "nav main" "foot ...."; /* much easier way to see the grid! */ } .grid-item { grid-area: foot; /* much easier to place the item! */ }

We can actually see a grid’s line numbers and grid areas in DevTools. In Firefox, for example, go to the Layout panel. Then, under the Grid tab, locate the “Grid display settings” and enable the “Display line number” and “Display area names” options.

This ASCII approach using named areas requires a lot less effort to visualize and easily find the placement of elements.

Let’s look at the “universal” use case

Whenever I see a tutorial on named grid areas, the common example is generally some layout pattern containing header, main, sidebar, and footer areas. I like to think of this as the “universal” use case since it casts such a wide net.

It’s a great example to illustrate how grid-template-areas works, but a real-life implementation usually involves media queries set to change the layout at certain viewport widths. Rather than having to re-declare grid-area on each grid item at each breakpoint to re-position everything, we can use grid-template-areas to “respond” to the breakpoint instead — and get a nice visual of the layout at each breakpoint in the process!

Before defining the layout, let’s assign an ident to each element using the grid-area property as a base style.

header { grid-area: head; } .left-side { grid-area: left; } main { grid-area: main; } .right-side { grid-area: right; } footer { grid-area: foot; }

Now, let’s define the layout again as a base style. We’re going with a mobile-first approach so that things will stack by default:

.grid-container { display: grid; grid-template-areas: "head" "left" "main" "right" "foot"; }

Each grid item is auto-sized in this configuration — which seems a little bit weird — so we can set min-height: 100vh on the grid container to give us more room to work with:

.grid-container { display: grid; grid-template-areas: "head" "left" "main" "right" "foot"; min-height: 100vh; }

Now let’s say we want the main element to sit to the right of the stacked left and right sidebars when we get to a slightly wider viewport width. We re-declare grid-template-areas with an updated ASCII layout to get that:

@media (min-width: 800px) { .parent { grid-template-columns: 0.5fr 1fr; grid-template-rows: 100px 1fr 1fr 100px; grid-template-areas: "head head" "left main" "right main" "foot foot"; } }

I tossed some column and row sizing in there purely for aesthetics.

As the browser gets even wider, we may want to change the layout again, so that main is sandwiched between the left and right sidebars. Let’s write the layout visually!

.grid-container { grid-template-columns: 200px 1fr 200px; /* again, just for sizing */ grid-template-areas: "head head head" "left main right" "left main right" "foot foot foot"; } CodePen Embed Fallback Leveraging implicit line names for flexibility

According to the spec, grid-template-areas automatically generates names for the grid lines created by named grid areas. We call these implicitly-named grid lines because they are named for us for free without any additional work.

Every named grid area gets four implicitly-named grid lines, two in the column direction and two in the row direction, where -start and -end are appended to the ident. For example, a grid area named head gets head-start and head-end lines in both directions for a total of four implicitly-named grid lines.

We can use these lines to our advantage! For instance, if we want an element to overlay the main, left, and right areas of our grid. Earlier, we talked about how layouts have to be rectangular — no “T” and “L” shaped layouts allowed. Consequently, we’re unable to use the ASCII visual layout method to place the overlay. We can, however, use our implicit line names using the same grid-area property on the overlay that we use to position the other elements.

Did you know that grid-area is a shorthand property, sort of the same way that margin and padding are shorthand properties? It takes multiple values the same way, but instead of following a “clockwise” direction like, margin — which goes in order of margin-block-start, margin-inline-end, margin-block-end, and margin-inline-start — grid-area goes like this:

grid-area: block-start / inline-start / block-end / inline-end;

But we’re talking about rows and columns, not block and inline directions, right? Well, they correspond to one another. The row axis corresponds to the block direction, and the column axis corresponds to the inline direction:

grid-area: grid-row-start / grid-column-start / grid-row-end / grid-column-end;

Back to positioning that overlay element as a grid item in our layout. The grid-area property will be helpful to position the element using our implicitly-named grid lines:

.overlay { grid-area: left-start / left-start / right-end / main-end; } CodePen Embed Fallback Creating a minimal grid system

When we focus on layouts like the “universal” use case we just saw, it’s tempting to think of grid areas in terms of one area per element. But it doesn’t have to work like that. We can repeat idents to reserve more space for them in the layout. We saw that when we repeated the head and foot idents in the last example:

.grid-container { grid-template-areas: "head head head" "left main right" "left main right" "foot foot foot"; }

Notice that main, left, and right are also repeated but in the block direction.

Let’s forget about full page layouts and use named grid areas on a component. Grid is just as good for component layouts as full pages!

Here’s a pretty standard hero component that sports a row of images followed by different blocks of text:

The HTML is pretty simple:

<div class="hero"> <div class="image"> <img src="..." alt="" /> </div> <div class="text"> <!-- ... --> </div> </div>

We could do this for a real fast stacked layout:

.hero { grid-template-areas: "image" "text"; }

But then we have to reach for some padding, max-width or whatever to get the text area narrower than the row of images. How about we expand our ASCII layout into a four-column grid instead by repeating our idents on both rows:

.hero { display: grid; grid-template-columns: repeat(4, 1fr); /* maintain equal sizing */ grid-template-areas: "image image image image" "text text text text"; }

Alright, now we can place our grid items into those named areas:

.hero .image { grid-area: image; } .hero .text { grid-area: text; }

So far, so good — both rows take up the entire width. We can use that as our base layout for small screens.

But maybe we want to introduce the narrower text when the viewport reaches a larger width. We can use what we know about the full-stop character to “skip” columns. Let’s have the text ident skip the first and last columns in this case.

@media (min-width: 800px) { main { grid-template-columns: repeat(6, 1fr); /* increase to six columns */ grid-template-areas: "image image image image image image" "..... text text text text ....."; } }

Now we have the spacing we want:

If the layout needs additional tweaking at even larger breakpoints, we can add more columns and go from there:

.hero { grid-template-columns: repeat(8, 1fr); grid-template-areas: "image image image image image image image image" "..... text text text text text text ....."; }

Dev tool visualization:

Remember when 12-column and 16-column layouts were the big things in CSS frameworks? We can quickly scale up to that and maintain a nice visual ASCII layout in the code:

main { grid-template-columns: repeat(12, 1fr); grid-template-areas: "image image image image image image image image image image image image" "..... text text text text text text text text text text ....."; } CodePen Embed Fallback Let’s look at something more complex

We’ve looked at one fairly generic example and one relatively straightforward example. We can still get nice ASCII layout visualizations with more complex layouts.

Let’s work up to this:

I’ve split this up into two elements in the HTML, a header and a main:

<header> <div class="logo"> ... </div> <div class="menu"> ... </div> </header> <main> <div class="image"> ... </div> <h2> ... </h2> <div class="image"> ... </div> <div class="image"> ... </div> </main>

I think flexbox is more appropriate for the header since we can space its child elements out easily that way. So, no grid there:

header { display: flex; justify-content: space-between; /* etc. */ }

But grid is well-suited for the main element’s layout. Let’s define the layout and assign the idents to the corresponding elements that we need to position the .text and three .image elements. We’ll start with this as our baseline for small screens:

.grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-areas: "image1 image1 ..... image2" "texts texts texts texts" "..... image3 image3 ....."; }

You can already see where we’re going with this, right? The layout is visualized for us, and we can drop the grid items into place with the custom idents:

.image:nth-child(1) { grid-area: image1; } .image:nth-last-child(2) { grid-area: image2; } .image:nth-last-child(1) { grid-area: image3; } h2 { grid-area: texts; }

That’s our base layout, so let’s venture into a wider breakpoint:

@media (min-width: 800px) { .grid { grid-template-columns: repeat(8, 1fr); grid-template-areas: ". image1 image1 ...... ...... ...... image2 ." ". texts texts texts texts texts image2 ." ". ..... image3 image3 image3 image3 ...... ."; } }

I bet you know exactly how that will look because the layout is right there in the code!

Same deal if we decide to scale up even further:

.grid { grid-template-columns: repeat(12, 1fr); grid-template-areas: ". image1 image1 ..... ..... ..... ..... ..... ..... ..... ..... ." ". texts texts texts texts texts texts texts texts texts image2 ." ". ..... image3 image3 image3 image3 ..... ..... ..... ..... ..... ."; }

Here’s the full demo:

CodePen Embed Fallback

I’m using the “negative margin hack” to get the first image to overlap the heading.

Wrapping up

I’m curious if anyone else is using grid-template-areas to create named areas for the benefit of having an ASCII visual of the grid layout. Having that as a reference in my CSS code has helped de-mystify some otherwise complex designs that may have been even more complex when dealing with line numbers.

But if nothing else, defining grid layouts this way teaches us some interesting things about CSS Grid that we saw throughout this post:

  • The grid-template-areas property allows us to create custom idents — or “named areas” — and use them to position grid items using the grid-area property.
  • There are three types of “tokens” that grid-template-areas accepts as values, including named cell tokens, null cell tokens, and trash cell tokens.
  • Each row that is defined in grid-template-areas needs the same number of cells. Ignoring a single cell doesn’t create a layout; it is considered a trash token.
  • We can get a visual ASCII-like diagram of the grid layout in the grid-template-areas property value by using required whitespaces between named cell tokens while defining the grid layout.
  • Make sure there is no whitespace inside a null cell token (e.g. .....). Otherwise, a single whitespace between null cell tokens creates unnecessary empty cells, resulting in an invalid layout.
  • We can redefine the layout at various breakpoints by re-positioning the grid items using grid-area, then re-declaring the layout with grid-template-areas on the grid container to update the track listing, if needed. There’s no need to touch the grid items.
  • Custom named grid areas automatically get four implicitly assigned line names — <custom-ident>-start and <custom-ident>-end in both the column and row directions.

Using Grid Named Areas to Visualize (and Reference) Your Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Not Sure How to WordPress Anymore?

Css Tricks - Thu, 08/25/2022 - 4:34am

Neither do I! And that’s probably because there’s a lot happening in WordPress-land. The evolution towards full-site editing (FSE) introduces frequent changes to the way we build themes and plugins, and at such break-neck speed that the documentation itself is either non-existent or nearly stale upon being published. Heck, the term “full-site editing” might even change.

Tom McFarlin was musing about this in his post titled “Writing Tutorials in These Gutenberg Times”:

I know Gutenberg has been in development for five years and I know that it’s matured a lot over the course of that time. But [t]he number of tutorials explaining how to do something that’s already outdated was absolutely incredible.

The truth is that I wouldn’t know where to start if I was asked to make a new WordPress site. As I see, there are a number of ways to go in this evolving era of WordPress:

  • Make a virtually empty theme that leverages the Site Editor for templating and block patterns for layouts.
  • Make a child theme based on the existing Twenty Twenty-Two theme (because it supports FSE out of the box and is minimal enough to customize without much fuss).
  • Make a classic theme.
  • Ditch theming altogether and make a headless front-end that consumes the WordPress REST API.

I mean, we have so many tools for extending WordPress as a CMS that the front end of a WordPress site may vary from site to site. We can quite literally build an entire custom WordPress site with nothing but some tweaks to the theme.json file and fiddling around with layouts in the Block Editor.

It’s amazing and dizzying all at once.

It can also be frustrating, and we saw some of the frustration boil over when Matt Mullenweg commented on the recent design updates to the WordPress.org homepage and the amount of time took to complete:

[…] it’s such a basic layout, it’s hard to imagine it taking a single person more than a day on Squarespace, Wix, Webflow, or one of the WP page builders.

(And, yes, someone proved that a nearly identical copy of the design could be created in 20 minutes.)

I think Matt’s comments have more to do with the process and solving the right problems than they are criticizing the approach that was taken. But reading the comments on that post is a nice microcosm of what I believe is an existential dilemma that many WordPress developers — including myself — are feeling after five years of living between “classic” and FSE themes.

I’ll be honest: I feel super out of touch with FSE development. So out of touch that I’ve wondered whether I’ve fallen too far behind and whether I’ll be able to catch up. I know there’s a huge effort to bolster learning (Learn WordPress is a great example of that), but it feels like there’s still something missing — or some sorta disconnect — that’s preventing the community from being on the same page as far as where we are and where we’re heading.

Could it be a lack of communication? Nah, there’s lots of that, not to mention lots of opportunities to attend meetings and view meeting notes. Could it be a lack of stable documentation? That’s legit, at least when I’ve tried seeking information on block development.

Perhaps the biggest shortcoming is the dearth of blog posts that share tips, tricks, and best practices. The WordPress community has always been a vast army of folks who generously share their talents and wisdom. But I think Tom summed it up best when he tweeted:

my sympathy to anyone who duckduckgo’s/googles a tutorial for how to create a gutenberg block and cannot find a single consistent tutorial.

what a mess.

— Tom McFarlin (@tommcfarlin) August 17, 2022

I, for one, would love to be writing about WordPress as much as I have in the “classic” era. But again, there’s that elusive starting point that prevents me from feeling confident about anything I’d say.

Not Sure How to WordPress Anymore? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Using CSS Cascade Layers to Manage Custom Styles in a Tailwind Project

Css Tricks - Wed, 08/24/2022 - 3:11am

If a utility class only does one thing, chances are you don’t want it to be overridden by any styles coming from elsewhere. One approach is to use !important to be 100% certain the style will be applied, regardless of specificity conflicts.

The Tailwind config file has an !important option that will automatically add !important to every utility class. There’s nothing wrong with using !important this way, but nowadays there are better ways to handle specificity. Using CSS Cascade Layers we can avoid the heavy-handed approach of using !important.

Cascade layers allow us to group styles into “layers”. The precedence of a layer always beats the specificity of a selector. Specificity only matters inside each layer. Establishing a sensible layer order helps avoid styling conflicts and specificity wars. That’s what makes CSS Cascade Layers a great tool for managing custom styles alongside styles from third-party frameworks, like Tailwind.

A Tailwind source .css file usually starts something like this:

@tailwind base; @tailwind components; @tailwind utilities; @tailwind variants;

Let’s take a look at the official Tailwind docs about directives:

Directives are custom Tailwind-specific at-rules you can use in your CSS that offer special functionality for Tailwind CSS projects. Use the @tailwind directive to insert Tailwind’s base, components, utilities and variants styles into your CSS.

In the output CSS file that gets built, Tailwind’s CSS reset — known as Preflight — is included first as part of the base styles. The rest of base consists of CSS variables needed for Tailwind to work. components is a place for you to add your own custom classes. Any utility classes you’ve used in your markup will appear next. Variants are styles for things like hover and focus states and responsive styles, which will appear last in the generated CSS file.

The Tailwind @layer directive

Confusingly, Tailwind has its own @layer syntax. This article is about the CSS standard, but let’s take a quick look at the Tailwind version (which gets compiled away and doesn’t end up in the output CSS). The Tailwind @layer directive is a way to inject your own extra styles into a specified part of the output CSS file.

For example, to append your own styles to the base styles, you would do the following:

@layer base { h1 { font-size: 30px; } }

The components layer is empty by default — it’s just a place to put your own classes. If you were doing things the Tailwind way, you’d probably use @apply (although the creator of Tailwind recently advised against it), but you can also write classes the regular way:

@layer components { .btn-blue { background-color: blue; color: white; } }

The CSS standard is much more powerful. Let’s get back to that…

Using the CSS standard @layer

Here’s how we can rewrite this to use the CSS standard @layer:

@layer tailwind-base, my-custom-styles, tailwind-utilities; @layer tailwind-base { @tailwind base; } @layer tailwind-utilities { @tailwind utilities; @tailwind variants; }

Unlike the Tailwind directive, these don’t get compiled away. They’re understood by the browser. In fact, DevTools in Edge, Chrome, Safari, and Firefox will even show you any layers you’ve defined.

You can have as many layers as you want — and name them whatever you want — but in this example, all my custom styles are in a single layer (my-custom-styles). The first line establishes the layer order:

@layer tailwind-base, my-custom-styles, tailwind-utilities;

This needs to be provided upfront. Be sure to include this line before any other code that uses @layer. The first layer in the list will be the least powerful, and the last layer in the list will be the most powerful. That means tailwind-base is the least powerful layer and any code in it will be overridden by all the subsequent layers. That also means tailwind-utilities will always trump any other styles — regardless of source order or specificity. (Utilities and variants could go in separate layers, but the maintainers of Tailwind will ensure variants always trump utilities, so long as you include the variants below the utilities directive.)

Anything that isn’t in a layer will override anything that is in a layer (with the one exception being styles that use !important). So, you could also opt to leave utilities and variants outside of any layer:

@layer tailwind-base, tailwind-components, my-custom-styles; @layer tailwind-base { @tailwind base; } @layer tailwind-components { @tailwind components; } @tailwind utilities; @tailwind variants;

What did this actually buy us? There are plenty of times when advanced CSS selectors come in pretty handy. Let’s create a version of :focus-within that only responds to keyboard focus rather than mouse clicks using the :has selector (which lands in Chrome 105). This will style a parent element when any of its children receive focus. Tailwind 3.1 introduced custom variants — e.g. <div class="[&:has(:focus-visible)]:outline-red-600"> — but sometimes it’s easier to just write CSS:

@layer tailwind-base, my-custom-styles; @layer tailwind-base { @tailwind base; } @tailwind utilities; @layer my-custom-styles { .radio-container { padding: 4px 24px; border: solid 2px rgb(230, 230, 230); } .radio-container:has(:focus-visible) { outline: solid 2px blue; } }

Let’s say in just one instance we want to override the outline-color from blue to something else. Let’s say the element we’re working with has both the Tailwind class .outline-red-600 and our own .radio-container:has(:focus-visible) class:

<div class="outline-red-600 radio-container"> ... </div>

Which outline-color will win?

Ordinarily, the higher specificity of .radio-container:has(:focus-visible) would mean the Tailwind class has no effect — even if it’s lower in the source order. But, unlike the Tailwind @layer directive that relies on source order, the CSS standard @layer overrules specificity.

As a result, we can use complex selectors in our own custom styles but still override them with Tailwind’s utility classes when we need to — without having to resort to heavy-handed !important usage to get what we want.

Using CSS Cascade Layers to Manage Custom Styles in a Tailwind Project originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Expert Lists: Fonts for Books

Typography - Wed, 08/24/2022 - 1:00am

Read the book, Typographic Firsts

Our new Expert Lists series invites design experts to recommend fonts for specific uses. Johannes López Ayala, creative director at Tipogris Books and Brands, and lecturer at Rhine-Waal University of Applied Sciences, introduces some of his favorite typefaces for Books. A wonderful selection with some traditional faces but with an inspired sprinkling of contemporary flair.

The post Expert Lists: Fonts for Books appeared first on I Love Typography.

Removing jQuery from GOV.UK

Css Tricks - Tue, 08/23/2022 - 3:04am

The GOV.UK team recently published “How and why we removed jQuery from GOV.UK“. This was an insightful look at how an organization can assess its tooling and whether something is still the best tool for the job. This is not a nudge to strip libraries out of your current project right now! Many of us may still be supporting legacy projects and browser requirements that prevent this from being a viable option.

Some of the criticism appears to be that the library size argument is negligible on modern network speeds and caching.

GOV.UK posted an update to address this criticism with metrics – “The impact of removing jQuery on our web performance“.

This article also makes the case for improving maintenance. Instead of upgrading disparate outdated versions of code and having to address security updates in a piecemeal approach, removing the dependency reduces this footprint. This is the dream of having the luxury for addressing technical debt.

Previously, GitHub also documented how they incrementally decoupled jQuery from their front-end code. Improving maintenance and developer experience played a role into their decision.

What caught my eye in particular was the link to the documentation on how to remove jQuery. Understanding how to decouple and perform migration steps are maintenance tasks that will continue to come up for websites and it’s reassuring to have a guide from someone that had to do the same.

Further musing on this subject turned up the old chestnuts “You Might Not Need jQuery” (2014), “(Now More Than Ever) You Might Not Need jQuery” (2017), “Is jQuery still relevant? (1)” (2016), and “Is jQuery still relevant? (2)” (2017).

To Shared LinkPermalink on CSS-Tricks

Removing jQuery from GOV.UK originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

When Do You Use CSS Columns?

Css Tricks - Thu, 08/18/2022 - 3:24am

That ain’t rhetorical: I’m really interested in finding great use cases for CSS multi-column layouts.

The answer seems straightforward. Use columns when you want to split any content into columns, right? Here is generally the sort of example you’ll find in articles that show how CSS mutli-column layouts work, including our very own Almanac:

CodePen Embed Fallback

Right on. But is this an actual use case? Mmmmmaybe. If the text is relatively brief, then perhaps it’s a nice touch. That’s how I sold it to myself when redesigning my website a few years ago. It’s not that way today, but this is what it looked like then:

But an entire long-form article split into columns? I love it in newspapers but am hesitant to scroll down a webpage to read one column, only to scroll back up to do it again.

I suppose we can use it to place two elements side-by-side, but flexbox is way more suited for that. Plus, a limitation prevents us from selecting the columns to size them individually. The columns have to be the same width.

One thing columns have going for them is that they are the only CSS layout method that fragments content. (That is, unless we’re counting CSS Regions… what happened to those, anyway?!) So, if you wanna split a paragraph up into columns, it’s already possible without additional wrappers.

When else might you need to split a continuous block of content into columns? I remember needing to do that when I had a big ol’ unordered list of items. I like the way lists can make content easy to scan, but long lists can make one side of the page look super heavy. Let’s say, for example, that we were listing out all the post tags for CSS-Tricks in alphabetical groups. A multi-column layout works beautifully for that:

CodePen Embed Fallback

Go ahead and try resizing the viewport width. Three columns are defined but the number will change based on the amount of available space. Gotta love all that responsive goodness without the media query work!

I was working on a demo for the :left pseudo-class and reached for columns because it’s a great way to fragment things for printing demos. So, I guess there’s another use case. And while making a demo, I realized that a multi-column layout could be used to create a masonry grid of items, like an image gallery:

CodePen Embed Fallback

But what else? Are we limited to short paragraphs, long lists, and free-flowing grids?

When Do You Use CSS Columns? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Why (and How) I Write Code With Pencil and Paper

Css Tricks - Wed, 08/17/2022 - 3:21am

If the thought of handwriting code seems silly, it might surprise you to know that it’s inevitable. If you’re unsure, think about the last job interview you did, and remember how there was no computer around in the interview room — just your interviewers, a blank sheet of paper, and a blue ball-point pen.

For the students among you, it’s even a bigger deal as your grades hang in by the lines of code you had strategically squeezed into the available space in your answer sheet.

And not just that, experienced programmers can point you to the bundle of A4 sheets they had removed from the office copy machine to scribble down a particularly complex algorithm they had been working on.

So whether you’re an exam student, potential job interviewee, or someone wanting to resolve their programming dead ends, I hope this article helps you out when you put your pen to the paper to code.

Although I will focus on the analog aspect of writing code, you can apply these steps to coding in any form or language. So consider this to be also like a generic coding guideline that works specifically for me but can also be very useful to you in your work.

Why write it down?

Before we start, it’s essential to understand that no one expects you to jot down production-ready code in a notebook. It’s not like you can drop that into a code editor and compile it without an error. If producing perfect code was the goal, you would be seated in front of a computer in the interview rooms and exam halls.

The purpose of handwriting code is to work through logic in advance. There’s s desire to “get in the browser” as soon as possible in design, but there is conventional wisdom in sketching designs by hand. A low-fidelity medium encourages quick experimentation and inexpensive mistakes.

The toil of trying to figure out how to affect surrounding items with one click (from my last article)

The same can be true of code, mainly when working out syntax and semantics. That said, getting the correct syntax and semantics is always a plus point, though not the sole focus of the whole handwriting exercise.

Let’s see where we can start when it comes to handwriting code.

Know your question

During my final year in college, I couldn’t do an internship or even attend campus interviews because of health reasons. As a result, my very first job interview was quite literal with high stakes.

When I look back now, the interview was pretty easy. But having never attended one before, I was anxious beyond reason. The first thing the interviewers asked about programming was if I could output an inverted triangle made of asterisks. As I said, it was easy — nothing a for loop can’t handle, right? But like I said, my anxiety was through the roof as well.

I took a deep breath, pressed my palm against the blank sheet of paper they had laid out for me, slid it as slow as possible towards me on the table (buying time, of course), clicked the pen, and then I did something right.

I first drew an inverted triangle made of asterisks. That’s how I found my feet on the ground to start answering their question.

I’ve seen otherwise brilliant developers get something wrong simply because they never fully grasp what it is they are solving.

The questions we work with are not like the questions physicists or mathematicians solve. They get a set of parameters and find the missing ones; our questions are also our results. We are already told what our results are — we have to figure out how to reach them. That’s why it’s imperative to know the question well because you’ll see the result.

Writing down or drawing out what you want to output is one of the best ways to start your coding. I understand that in our fast-paced industry, the expectation is that we have to jump right into the programming by running a “hello world” demo. And that’s great to familiarize yourself with an unfamiliar syntax and shake off your anxiousness about trying something new.

But when someone asks you a question and gives you a result to work up to, wouldn’t it just be better to put that down first? That question/result is not only your starting point but also your point of reference. At any step in your coding, you can look at it to ensure you’re working towards it and that you’re on the right track.

So whether in your answer sheets or in that blank A4 paper you’re about to write in, start by taking a second and writing down what it is you’re trying to output. You can put it in the margins or a corner if you don’t want it to be a part of your answer. Just make sure it’s somewhere where you can keep referencing it.

Outline your code

This step is like a double-edged sword. It can get you a roadmap to your program or waste your time. My job is to make sure it’s the former.

So, first and foremost, I like to say: outlining code is unnecessary if the scope of your problem or question is small. Again, this practice is neither prescriptive nor universal to all projects or situations. Imagine I’m your interviewer, and I ask you to write how to center an element in a web page using CSS in as many ways as possible. You won’t exactly be needing an outline for this. The code snippets are relatively small for each method.

But now, let’s say I assign you to write a web application that captures user signatures via a touchscreen interface and then saves the signature on the server. Not so straightforward, right? You’ve more than one thing to figure out. Perhaps, a little outline can help.

  1. UI for capturing signature — HTML Canvas? WebGL?
  2. Disable pointer events on the rest of the web page when the user is signing
  3. Convert and save the captured image to a PNG file — JS
  4. Then convert it to blob (maybe) and save it to the visitor’s log data table.

I’ve written a rough sequence of actions I think I might have to code. It could’ve been shorter or longer, depending on what I wanted from it.

I highly recommend outlining code for client projects. Write the outline along with your user requirements or on the back of wireframes you’ve printed out.

Your quick snapshot of bullet points gives you a map, a to-do list, and a checklist to verify against when you reach the end of the project — pretty much your whole project’s summary in a low-fidelity list. It can also become a template to start your next similar project.

But like I said before, this step is like a double-edged sword. You’ll have to keep this short for examinees and interviewees when there are time constraints.

If you don’t know where to start, write down just three essential functions you’ll have to code in your application, and if you have got the time, make it five.

But that’s about it. Spend as little time as possible on this, and don’t sweat over the details. The outline is not going to score you extra points. It’s there only to help you make sure you have everything covered. It captures your initial gut reaction and keeps you honest throughout the project’s life.

Longhand vs. shorthand A quick reference to disable text selection

Time to start coding. So, what do you write? “Bdrs” or “border-radius“; “div -> p” or “<div><p></div></p>“; “pl()” or “println()“; “q()” or “querySelector()“?

If someone else is grading your code, then there’s no choice. Leave out abbreviations, pseudo-codes, Emmet shortcuts, and any other form of shorthand writing. Otherwise, there’s no reason to assume that anyone reading this knows what your abbreviations mean.

It’s really up to you.

If you’ve gotten out of touch with writing by hand — and many of us have — it’s better not to go overboard with the longhand notations, as they get tedious. At the same time, there’s no such thing as being too frugal with your writing. Not if you want to be able to look back on it one day and understand what you’d written down.

I have an open file in my note-taking app and a lined notepad on my desk where I write down code snippets I want to save for later reference. They are unorganized, just a long stream of snippets. That’s why when I browse through older notes, I wouldn’t know what I meant to write if I had not written them down clearly.

I forget syntaxes all the time. For instance, I’ve been using the arrow notation for JavaScript functions since it was introduced (because it’s shorter), and I’m pretty sure if someone suddenly asks me to define a function using the function keyword, I might even misplace the parentheses or the function name, inciting a syntax error.

It’s not unusual for us to forget syntaxes we haven’t used in a while. That’s why it’s better to write your notes clearly when you know you need them for future reference.

The non-sequential flow of code

Unlike the last step, which doesn’t apply to those of you interviewees and test-takers, this one is catered especially to you.

Most programming languages are interpreted, compiled, and executed so that sometimes pre-written code in the source is executed later when called. We do it in JavaScript, for example, with function calling — functions can be defined initially, then executed later. Examinees and interviewees can use this to start working on the critical point of your answer first.

As I’ve said from the very beginning, the purpose of handwriting code is to work through or test the logic of whatever it is you program. It’s best when you focus on resolving that first.

Let’s take a classic textbook example — a program to find the nth Fibonacci number. If I were to write a simple outline for it, it would be something like this:

  1. Get the input.
  2. Calculate the Fibonacci number.
  3. Summarise the output.
  4. Print the output.

All the steps in that outline are essential; however, 1, 3, and 4 are more obligatory. They are necessary but not important enough to focus on right away.

It’s better to start writing down the code to calculate the Fibonacci number rather than to fetch the input. Wrap it in a function, then go ahead and write the code sequentially and write down a line to call that function where appropriate.

Spend your time writing code that focuses on the heart of the problem.

Real professionals can skip ahead. Let’s say I have a client project, and I have to work with some triangle geometry — got two sides, opposite angle, and gotta find the third side’s length. And I’ve decided to scribble on paper to get started rather than opening my IDE.

First, I would draw the triangle, of course (that’s something I’m very experienced with, as you can tell). I would write down some sample lengths and angles. Then I’d write the formula (compliments of online searching, for sure), and then I’d jump right to the code for the function.

There’s no point in me writing down the obligatory steps even though I’ll need them in production-ready code. But it would be different if I had to write that on an answer sheet in an exam. I can’t skip the other steps; however, I can still start with the code for the formula.

Pseudo-code

Chris has already written a handy article on pseudo-code that I highly recommend you give a solid read.

For all those professionals who feel like the whole handwriting code thing doesn’t seem like your cup of tea but still might be curious if it can help you, then pseudo-code might be the balance you’re looking for.

It’s similar to outlining the code, as I mentioned in one of the previous steps. However, it’s briefer and feels more like shorthand coding. It’s sometimes also referred to as “skeleton code.”

Here’s some quick pseudo-code for a CSS grid layout:

Grid 5 60px rows 6 100px columns

There isn’t much to write! So, even though putting a pencil to paper is excellent for this sort of thing, it’s just as effective, fast, and inexpensive to jot some pseudo code into whatever program you’re using.

Space and comments

I believe code is 90% keywords and 10% tabs. Withoutspacesthereadabilityofwordsdecreases. Indentations are necessary for handwritten code as well. However, please don’t use it for every level because the width of the paper will limit you. Use spaces judiciously, but use them.

Prized OG snippet, written with extra TLC

If you’re writing code for your use, I also believe that if you’ve followed everything I’ve mentioned so far and have already written down your output and an outline on the page, you may not even need to include comments. Comments tell you quickly what its following set of code does. If you have already written and read an outline for the code, then comments are redundant notes.

However, if your judgment says to put down one, then do it. Add it to the right side of the code (since you won’t be able to insert it between already written lines the way you could in, say, VS Code). Use forward slashes, brackets, or arrows to denote that they are comments.

For examinees who are unconfident with a certain syntax, write down comments. This way, at least, you’re letting the person grading your paper know your intention with that incorrectly formatted code. And use only the correct delimiters to denote comments — for example, that would be the forward slashes for JavaScript.

Analog vs. digital

As I mentioned earlier, everything I’m providing here can is generic coding advice. If you don’t want to try this with physical paper, any note-taking application also works.

But if you’re going to try the digital route, my recommendation is to try using something other than a straight note-taking app. Work with more visual digital tools — flow diagrams, mind maps, wireframes, etc. They can help you visualize your result, the outlines, and the code itself.

I am not much of a digital citizen (except for working on the web and recently converting to reading e-books), so I stick to physical notebooks.

My favorite tools for handwriting code

Any pencil and paper will do! But there are lots of options out there, and these are a few choice tools I use:

There is no “write” way to code

I hope, if nothing else, my little way of handwriting code with pencil and paper makes you evaluate the way you already plan and write code. I like knowing how other developers approach their work, and this is my way of giving you a peek into the way I do things.

Again, nothing here is scientific or an exact art. But if you want to give handwritten code planning a try, here’s everything we’ve covered in a nice ordered list:

  1. Start by writing down (with sample data, if needed) the output of your code.
  2. Write an outline for the code. Please keep it to three steps for small projects or ones that are less complex.
  3. Use longhand notations. Developers writing for themselves can use shorthand notations as long as the writing is legible and makes sense to you when you refer to it later.
  4. When under a time constraint, consider writing the code that tackles the heart of the problem first. Later, write down a call to that code at the right place in your sequential code.
  5. If you feel confident, try writing pseudo code addressing the main idea.
  6. Use proper indentations and spaces — and be mindful of the paper’s width.

That’s it! When you’re ready to try writing code by hand, I hope this article makes it easy for you to start. And if you’re sitting down for an exam or an interview, I hope this helps you focus on getting the questions right.

Why (and How) I Write Code With Pencil and Paper originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS Grid and Custom Shapes, Part 1

Css Tricks - Mon, 08/15/2022 - 3:13am

In a previous article, I looked at CSS Grid’s ability to create complex layouts using its auto-placement powers. I took that one step further in another article that added a zooming hover effect to images in a grid layout. This time, I want to dive into another type of grid, one that works with shapes.

Like, what if the images aren’t perfectly square but instead are shaped like hexagons or rhombuses? Spoiler alert: we can do it. In fact, we’re going to combine CSS Grid techniques we’ve looked at and drop in some CSS clip-path and mask magic to create fancy grids of images for just about any shape you can imagine!

Let’s start with some markup

Most of the layouts we are going to look at may look easy to achieve at first glance, but the challenging part is to achieve them with the same HTML markup. We can use a lot of wrappers, divs, and whatnot, but the goal of this post is to use the same and smallest amount of HTML code and still get all the different grids we want. After all, what’s CSS but a way to separate styling and markup? Our styling should not depend on the markup, and vice versa.

This said, let’s start with this:

<div class="gallery"> <img src="..." alt="..."> <img src="..." alt="..."> <img src="..." alt="..."> <img src="..." alt="..."> <!-- as many times as we want --> </div>

A container with images is all that we need here. Nothing more!

CSS Grid of Hexagons

This is also sometimes referred to as a “honeycomb” grid.

CodePen Embed Fallback

There are already plenty of other blog posts out there that show how to make this. Heck, I wrote one here on CSS-Tricks! That article is still good and goes way deep on making a responsive layout. But for this specific case, we are going to rely on a much simpler CSS approach.

First, let’s use clip-path on the images to create the hexagon shape and we place all of them in the same grid area so they overlap.

.gallery { --s: 150px; /* controls the size */ display: grid; } .gallery > img { grid-area: 1/1; width: var(--s); aspect-ratio: 1.15; object-fit: cover; clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%); } clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%)

Nothing fancy yet. All the images are hexagons and above each other. So it looks like all we have is a single hexagon-shaped image element, but there are really seven.

CodePen Embed Fallback

The next step is to apply a translation to the images to correctly place them on the grid.

Notice that we still want one of the images to remain in the center. The rest are placed around it using CSS translate and good ol’ fashioned geometry. Here’s are the mock formulas I came up with for each image in the grid:

translate((height + gap)*sin(0deg), (height + gap)*cos(0)) translate((height + gap)*sin(60deg), (height + gap)*cos(60deg)) translate((height + gap)*sin(120deg), (height + gap)*cos(120deg)) translate((height + gap)*sin(180deg), (height + gap)*cos(180deg)) translate((height + gap)*sin(240deg), (height + gap)*cos(240deg)) translate((height + gap)*sin(300deg), (height + gap)*cos(300deg))

A few calculations and optimization later (let’s skip that boring part, right?) we get the following CSS:

.gallery { --s: 150px; /* control the size */ --g: 10px; /* control the gap */ display: grid; } .gallery > img { grid-area: 1/1; width: var(--s); aspect-ratio: 1.15; object-fit: cover; clip-path: polygon(25% 0%, 75% 0%, 100% 50% ,75% 100%, 25% 100%, 0 50%); transform: translate(var(--_x,0), var(--_y,0)); } .gallery > img:nth-child(1) { --_y: calc(-100% - var(--g)); } .gallery > img:nth-child(7) { --_y: calc( 100% + var(--g)); } .gallery > img:nth-child(3), .gallery > img:nth-child(5) { --_x: calc(-75% - .87*var(--g)); } .gallery > img:nth-child(4), .gallery > img:nth-child(6) { --_x: calc( 75% + .87*var(--g)); } .gallery > img:nth-child(3), .gallery > img:nth-child(4) { --_y: calc(-50% - .5*var(--g)); } .gallery > img:nth-child(5), .gallery > img:nth-child(6) { --_y: calc( 50% + .5*var(--g)); }

Maybe that’ll be easier when we get real trigonometry functions in CSS!

Each image is translated by the --_x and --_y variables that are based on those formulas. Only the second image (nth-child(2)) is undefined in any selector because it’s the one in the center. It can be any image if you decide to use a different order. Here’s the order I’m using:

With only a few lines of code, we get a cool grid of images. To this, I added a little hover effect to the images to make things fancier.

Guess what? We can get another hexagon grid by simply updating a few values.

CodePen Embed Fallback

If you check the code and compare it with the previous one you will notice that I have simply swapped the values inside clip-path and I switched between --x and --y. That’s all!

CSS Grid of Rhombuses

Rhombus is such a fancy word for a square that’s rotated 45 degrees.

CodePen Embed Fallback

Same HTML, remember? We first start by defining a 2×2 grid of images in CSS:

.gallery { --s: 150px; /* controls the size */ display: grid; gap: 10px; grid: auto-flow var(--s) / repeat(2, var(--s)); place-items: center; } .gallery > img { width: 100%; aspect-ratio: 1; object-fit: cover; }

The first thing that might catch your eye is the grid property. It’s pretty uncommonly used but is super helpful in that it’s a shorthand that lets you define a complete grid in one declaration. It’s not the most intuitive — and not to mention readable — property, but we are here to learn and discover new things, so let’s use it rather than writing out all of the individual grid properties.

grid: auto-flow var(--s) / repeat(2,var(--s)); /* is equivalent to this: */ grid-template-columns: repeat(2, var(--s)); grid-auto-rows: var(--s);

This defines two columns equal to the --s variable and sets the height of all the rows to --s as well. Since we have four images, we will automatically get a 2×2 grid.

Here’s another way we could have written it:

grid-template-columns: repeat(2, var(--s)); grid-template-rows: repeat(2, var(--s));

…which can be reduced with the grid shorthand:

grid: repeat(2,var(--s)) / repeat(2,var(--s));

After setting the grid, we rotate it and the images with CSS transforms and we get this:

CodePen Embed Fallback

Note how I rotate them both by 45deg, but in the opposite direction.

.gallery { /* etc. */ transform: rotate(45deg); } .gallery > img { /* etc. */ transform: rotate(-45deg); }

Rotating the images in the negative direction prevents them from getting rotated with the grid so they stay straight. Now, we apply a clip-path to clip a rhombus shape out of them.

clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%) CodePen Embed Fallback

We are almost done! We need to rectify the size of the image to make them fit together. Otherwise, they’re spaced far apart to the point where it doesn’t look like a grid of images.

The image is within the boundary of the green circle, which is the inscribed circle of the grid area where the image is placed. What we want is to make the image bigger to fit inside the red circle, which is the circumscribed circle of the grid area.

Don’t worry, I won’t introduce any more boring geometry. All you need to know is that the relationship between the radius of each circle is the square root of 2 (sqrt(2)). This is the value we need to increase the size of our images to fill the area. We will use 100%*sqrt(2) = 141% and be done!

.gallery { --s: 150px; /* control the size */ display: grid; grid: auto-flow var(--s) / repeat(2,var(--s)); gap: 10px; place-items: center; transform: rotate(45deg); } .gallery > img { width: 141%; /* 100%*sqrt(2) = 141% */ aspect-ratio: 1; object-fit: cover; transform: rotate(-45deg); clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }

Like the hexagon grid, we can make things fancier with that nice zooming hover effect:

CodePen Embed Fallback CSS Grid of Triangular Shapes CodePen Embed Fallback

You probably know by now that the big trick is figuring out the clip-path to get the shapes we want. For this grid, each element has its own clip-path value whereas the last two grids worked with a consistent shape. So, this time around, it’s like we’re working with a few different triangular shapes that come together to form a rectangular grid of images.

The three images at the top The three images at the bottom

We place them inside a 3×2 grid with the following CSS:

.gallery { display: grid; gap: 10px; grid-template-columns: auto auto auto; /* 3 columns */ place-items: center; } .gallery > img { width: 200px; /* controls the size */ aspect-ratio: 1; object-fit: cover; } /* the clip-path values */ .gallery > img:nth-child(1) { clip-path: polygon(0 0, 50% 0, 100% 100% ,0 100%); } .gallery > img:nth-child(2) { clip-path: polygon(0 0, 100% 0, 50% 100%); } .gallery > img:nth-child(3) { clip-path: polygon(50% 0, 100% 0, 100% 100%, 0 100%); } .gallery > img:nth-child(4) { clip-path: polygon(0 0, 100% 0, 50% 100%, 0 100%); } .gallery > img:nth-child(5) { clip-path: polygon(50% 0, 100% 100%, 0% 100%); } .gallery > img:nth-child(6) { clip-path: polygon(0 0, 100% 0 ,100% 100%, 50% 100%); } }

Here’s what we get:

CodePen Embed Fallback

The final touch is to make the width of the middle column equal 0 to get rid of the spaces between the images. The same sort of spacing problem we had with the rhombus grid, but with a different approach for the shapes we’re using:

grid-template-columns: auto 0 auto;

I had to fiddle with the clip-path values to make sure they would all appear to fit together nicely like a puzzle. The original images overlap when the middle column has zero width, but after slicing the images, the illusion is perfect:

CSS Pizza Pie Grid

Guess what? We can get another cool grid by simply adding border-radius and overflow to our grid or triangular shapes. &#x1f389;

CodePen Embed Fallback CSS Grid of Puzzle Pieces

This time we are going to play with the CSS mask property to make the images look like pieces of a puzzle.

CodePen Embed Fallback

If you haven’t used mask with CSS gradients, I highly recommend this other article I wrote on the topic because it’ll help with what comes next. Why gradients? Because that’s what we’re using to get the round notches in the puzzle piece shapes.

Setting up the grid should be a cinch by now, so let’s focus instead on the mask part.

CodePen Embed Fallback

As illustrated in the above demo, we need two gradients to create the final shape. One gradient creates a circle (the green part) and the other creates the right curve while filling in the top part.

--g: 6px; /* controls the gap */ --r: 42px; /* control the circular shapes */ background: radial-gradient(var(--r) at left 50% bottom var(--r), green 95%, #0000), radial-gradient(calc(var(--r) + var(--g)) at calc(100% + var(--g)) 50%, #0000 95%, red) top/100% calc(100% - var(--r)) no-repeat;

Two variables control the shape. The --g variable is nothing but the grid gap. We need to account for the gap to correctly place our circles so they overlap perfectly when the whole puzzle is assembled. The --r variable controls the size of circular parts of the puzzle shape.

Now we take the same CSS and update a few values in it to create the three other shapes:

CodePen Embed Fallback

We have the shapes, but not the overlapping edges we need to make them fit together. Each image is limited to the grid cell it’s in, so it makes sense why the shapes are sort of jumbled at the moment:

We need to create an overflow by increasing the height/width of the images. From the above figure, we have to increase the height of the first and fourth images while we increase the width of the second and third ones. You have probably already guessed that we need to increase them using the --r variable.

.gallery > img:is(:nth-child(1),:nth-child(4)) { width: 100%; height: calc(100% + var(--r)); } .gallery > img:is(:nth-child(2),:nth-child(3)) { height: 100%; width: calc(100% + var(--r)); }

We are getting closer!

CodePen Embed Fallback

We created the overlap but, by default, our images either overlap on the right (if we increase the width) or the bottom (if we increase the height). But that’s not what we want for the second and fourth images. The fix is to use place-self: end on those two images and our full code becomes this:

CodePen Embed Fallback

Here is another example where I am using a conic gradient instead of a radial gradient. This gives us triangular puzzle pieces while keeping the same underlying HTML and CSS.

CodePen Embed Fallback

A last one! This time I am using clip-path and since it’s a property we can animate, we get a cool hover by simply updating the custom property that controls the shape.

CodePen Embed Fallback Wrapping up

That’s all for this first part! By combining the things we’ve already learned about CSS Grid with some added clip-path and mask magic, we were able to make grid layouts featuring different kinds of shapes. And we used the same HTML markup each time! And the markup itself is nothing more than a container with a handful of image elements!

In the second part, we are going to explore more complex-looking grids with more fancy shapes and hover effects.

I’m planning to take the demo of expanding image panels we made together in this other article:

CodePen Embed Fallback

…and transform it into a zig-zag image panels! And this is only one example among the many we will discover in the next article.

CSS Grid and Custom Shapes, Part 1 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

New business wanted

QuirksBlog - Thu, 09/30/2021 - 12:22am

Last week Krijn and I decided to cancel performance.now() 2021. Although it was the right decision it leaves me in financially fairly dire straits. So I’m looking for new jobs and/or donations.

Even though the Corona trends in NL look good, and we could probably have brought 350 people together in November, we cannot be certain: there might be a new flare-up. More serious is the fact that it’s very hard to figure out how to apply the Corona checks Dutch government requires, especially for non-EU citizens. We couldn’t figure out how UK and US people should be tested, and for us that was the straw that broke the camel’s back. Cancelling the conference relieved us of a lot of stress.

Still, it also relieved me of a lot of money. This is the fourth conference in a row we cannot run, and I have burned through all my reserves. That’s why I thought I’d ask for help.

So ...

Has QuirksMode.org ever saved you a lot of time on a project? Did it advance your career? If so, now would be a great time to make a donation to show your appreciation.

I am trying my hand at CSS coaching. Though I had only few clients so far I found that I like it and would like to do it more. As an added bonus, because I’m still writing my CSS for JavaScripters book I currently have most of the CSS layout modules in my head and can explain them straight away — even stacking contexts.

Or if there’s any job you know of that requires a technical documentation writer with a solid knowledge of web technologies and the browser market, drop me a line. I’m interested.

Anyway, thanks for listening.

position: sticky, draft 1

QuirksBlog - Wed, 09/08/2021 - 7:44am

I’m writing the position: sticky part of my book, and since I never worked with sticky before I’m not totally sure if what I’m saying is correct.

This is made worse by the fact that there are no very clear tutorials on sticky. That’s partly because it works pretty intuitively in most cases, and partly because the details can be complicated.

So here’s my draft 1 of position: sticky. There will be something wrong with it; please correct me where needed.

The inset properties are top, right, bottom and left. (I already introduced this terminology earlier in the chapter.)

h3,h4,pre {clear: left} section.scroll-container { border: 1px solid black; width: 300px; height: 250px; padding: 1em; overflow: auto; --text: 'scroll box'; float: left; clear: left; margin-right: 0.5em; margin-bottom: 1em; position: relative; font-size: 1.3rem; } .container,.outer-container { border: 1px solid black; padding: 1em; position: relative; --text: 'container'; } .outer-container { --text: 'outer container'; } :is(.scroll-container,.container,.outer-container):before { position: absolute; content: var(--text); top: 0.2em; left: 0.2em; font-size: 0.8rem; } section.scroll-container h2 { position: sticky; top: 0; background: white; margin: 0 !important; color: inherit !important; padding: 0.5em !important; border: 1px solid; font-size: 1.4rem !important; } .nowrap p { white-space: nowrap; } Introduction

position: sticky is a mix of relative and fixed. A sticky box takes its normal position in the flow, as if it had position: relative, but if that position scrolls out of view the sticky box remains in a position defined by its inset properties, as if it has position: fixed. A sticky box never escapes its container, though. If the container start or end scrolls past the sticky box abandons its fixed position and sticks to the top or the bottom of its container.

It is typically used to make sure that headers remain in view no matter how the user scrolls. It is also useful for tables on narrow screens: you can keep headers or the leftmost table cells in view while the user scrolls.

Scroll box and container

A sticky box needs a scroll box: a box that is able to scroll. By default this is the browser window — or, more correctly, the layout viewport — but you can define another scroll box by setting overflow on the desired element. The sticky box takes the first ancestor that could scroll as its scroll box and calculates all its coordinates relative to it.

A sticky box needs at least one inset property. These properties contain vital instructions, and if the sticky box doesn’t receive them it doesn’t know what to do.

A sticky box may also have a container: a regular HTML element that contains the sticky box. The sticky box will never be positioned outside this container, which thus serves as a constraint.

The first example shows this set-up. The sticky <h2> is in a perfectly normal <div>, its container, and that container is in a <section> that is the scroll box because it has overflow: auto. The sticky box has an inset property to provide instructions. The relevant styles are:

section.scroll-container { border: 1px solid black; width: 300px; height: 300px; overflow: auto; padding: 1em; } div.container { border: 1px solid black; padding: 1em; } section.scroll-container h2 { position: sticky; top: 0; } The rules Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Now let’s see exactly what’s going on.

A sticky box never escapes its containing box. If it cannot obey the rules that follow without escaping from its container, it instead remains at the edge. Scroll down until the container disappears to see this in action.

A sticky box starts in its natural position in the flow, as if it has position: relative. It thus participates in the default flow: if it becomes higher it pushes the paragraphs below it downwards, just like any other regular HTML element. Also, the space it takes in the normal flow is kept open, even if it is currently in fixed position. Scroll down a little bit to see this in action: an empty space is kept open for the header.

A sticky box compares two positions: its natural position in the flow and its fixed position according to its inset properties. It does so in the coordinate frame of its scroll box. That is, any given coordinate such as top: 20px, as well as its default coordinates, is resolved against the content box of the scroll box. (In other words, the scroll box’s padding also constrains the sticky box; it will never move up into that padding.)

A sticky box with top takes the higher value of its top and its natural position in the flow, and positions its top border at that value. Scroll down slowly to see this in action: the sticky box starts at its natural position (let’s call it 20px), which is higher than its defined top (0). Thus it rests at its position in the natural flow. Scrolling up a few pixels doesn’t change this, but once its natural position becomes less than 0, the sticky box switches to a fixed layout and stays at that position.

The sticky box has bottom: 0

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Sticky header

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

It does the same for bottom, but remember that a bottom is calculated relative to the scroll box’s bottom, and not its top. Thus, a larger bottom coordinate means the box is positioned more to the top. Now the sticky box compares its default bottom with the defined bottom and uses the higher value to position its bottom border, just as before.

With left, it uses the higher value of its natural position and to position its left border; with right, it does the same for its right border, bearing in mind once more that a higher right value positions the box more to the left.

If any of these steps would position the sticky box outside its containing box it takes the position that just barely keeps it within its containing box.

Details Sticky header

Very, very long line of content to stretch up the container quite a bit

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

The four inset properties act independently of one another. For instance the following box will calculate the position of its top and left edge independently. They can be relative or fixed, depending on how the user scrolls.

p.testbox { position: sticky; top: 0; left: 0; }

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

The sticky box has top: 0; bottom: 0

Regular content

Regular content

Regular content

Regular content

Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Setting both a top and a bottom, or both a left and a right, gives the sticky box a bandwidth to move in. It will always attempt to obey all the rules described above. So the following box will vary between 0 from the top of the screen to 0 from the bottom, taking its default position in the flow between these two positions.

p.testbox { position: sticky; top: 0; bottom: 0; } No container

Regular content

Regular content

Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

So far we put the sticky box in a container separate from the scroll box. But that’s not necessary. You can also make the scroll box itself the container if you wish. The sticky element is still positioned with respect to the scroll box (which is now also its container) and everything works fine.

Several containers Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside outer container

Content outside outer container

Or the sticky item can be several containers removed from its scroll box. That’s fine as well; the positions are still calculated relative to the scroll box, and the sticky box will never leave its innermost container.

Changing the scroll box Sticky header

The container has overflow: auto.

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

One feature that catches many people (including me) unaware is giving the container an overflow: auto or hidden. All of a sudden it seems the sticky header doesn’t work any more.

What’s going on here? An overflow value of auto, hidden, or scroll makes an element into a scroll box. So now the sticky box’s scroll box is no longer the outer element, but the inner one, since that is now the closest ancestor that is able to scroll.

The sticky box appears to be static, but it isn’t. The crux here is that the scroll box could scroll, thanks to its overflow value, but doesn’t actually do so because we didn’t give it a height, and therefore it stretches up to accomodate all of its contents.

Thus we have a non-scrolling scroll box, and that is the root cause of our problems.

As before, the sticky box calculates its position by comparing its natural position relative to its scroll box with the one given by its inset properties. Point is: the sticky box doesn’t scroll relative to its scroll box, so its position always remains the same. Where in earlier examples the position of the sticky element relative to the scroll box changed when we scrolled, it no longer does so, because the scroll box doesn’t scroll. Thus there is no reason for it to switch to fixed positioning, and it stays where it is relative to its scroll box.

The fact that the scroll box itself scrolls upward is irrelevant; this doesn’t influence the sticky box in the slightest.

Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

One solution is to give the new scroll box a height that is too little for its contents. Now the scroll box generates a scrollbar and becomes a scrolling scroll box. When we scroll it the position of the sticky box relative to its scroll box changes once more, and it switches from fixed to relative or vice versa as required.

Minor items

Finally a few minor items:

  • It is no longer necessary to use position: -webkit-sticky. All modern browsers support regular position: sticky. (But if you need to cater to a few older browsers, retaining the double syntax doesn’t hurt.)
  • Chrome (Mac) does weird things to the borders of the sticky items in these examples. I don’t know what’s going on and am not going to investigate.

Breaking the web forward

QuirksBlog - Thu, 08/12/2021 - 5:19am

Safari is holding back the web. It is the new IE, after all. In contrast, Chrome is pushing the web forward so hard that it’s starting to break. Meanwhile web developers do nothing except moan and complain. The only thing left to do is to pick our poison.

blockquote { font-size: inherit; font-family: inherit; } blockquote p { font-size: inherit; font-family: inherit; } Safari is the new IE

Recently there was yet another round of “Safari is the new IE” stories. Once Jeremy’s summary and a short discussion cleared my mind I finally figured out that Safari is not IE, and that Safari’s IE-or-not-IE is not the worst problem the web is facing.

Perry Sun argues that for developers, Safari is crap and outdated, emulating the old IE of fifteen years ago in this respect. He also repeats the theory that Apple is deliberately starving Safari of features in order to protect the app store, and thus its bottom line. We’ll get back to that.

The allegation that Safari is holding back web development by its lack of support for key features is not new, but it’s not true, either. Back fifteen years ago IE held back the web because web developers had to cater to its outdated technology stack. “Best viewed with IE” and all that. But do you ever see a “Best viewed with Safari” notice? No, you don’t. Another browser takes that special place in web developers’ hearts and minds.

Chrome is the new IE, but in reverse

Jorge Arango fears we’re going back to the bad old days with “Best viewed in Chrome.” Chris Krycho reinforces this by pointing out that, even though Chrome is not the standard, it’s treated as such by many web developers.

“Best viewed in Chrome” squares very badly with “Safari is the new IE.” Safari’s sad state does not force web developers to restrict themselves to Safari-supported features, so it does not hold the same position as IE.

So I propose to lay this tired old meme to rest. Safari is not the new IE. If anything it’s the new Netscape 4.

Meanwhile it is Chrome that is the new IE, but in reverse.

Break the web forward

Back in the day, IE was accused of an embrace, extend, and extinguish strategy. After IE6 Microsoft did nothing for ages, assuming it had won the web. Thanks to web developers taking action in their own name for the first (and only) time, IE was updated once more and the web moved forward again.

Google learned from Microsoft’s mistakes and follows a novel embrace, extend, and extinguish strategy by breaking the web and stomping on the bits. Who cares if it breaks as long as we go forward. And to hell with backward compatibility.

Back in 2015 I proposed to stop pushing the web forward, and as expected the Chrome devrels were especially outraged at this idea. It never went anywhere. (Truth to tell: I hadn’t expected it to.)

I still think we should stop pushing the web forward for a while until we figure out where we want to push the web forward to — but as long as Google is in charge that won’t happen. It will only get worse.

On alert

A blog storm broke out over the decision to remove alert(), confirm() and prompt(), first only the cross-origin variants, but eventually all of them. Jeremy and Chris Coyier already summarised the situation, while Rich Harris discusses the uses of the three ancient modals, especially when it comes to learning JavaScript.

With all these articles already written I will only note that, if the three ancient modals are truly as horrendous a security issue as Google says they are it took everyone a bloody long time to figure that out. I mean, they turn 25 this year.

Although it appears Firefox and Safari are on board with at least the cross-origin part of the proposal, there is no doubt that it’s Google that leads the charge.

From Google’s perspective the ancient modals have one crucial flaw quite apart from their security model: they weren’t invented there. That’s why they have to be replaced by — I don’t know what, but it will likely be a very complicated API.

Complex systems and arrogant priests rule the web

Thus the new embrace, extend, and extinguish is breaking backward compatibility in order to make the web more complicated. Nolan Lawson puts it like this:

we end up with convoluted specs like Service Worker that you need a PhD to understand, and yet we still don't have a working <dialog> element.

In addition, Google can be pretty arrogant and condescending, as Chris Ferdinandi points out.

The condescending “did you actually read it, it’s so clear” refrain is patronizing AF. It’s the equivalent of “just” or “simply” in developer documentation.

I read it. I didn’t understand it. That’s why I asked someone whose literal job is communicating with developers about changes Chrome makes to the platform.

This is not isolated to one developer at Chrome. The entire message thread where this change was surfaced is filled with folks begging Chrome not to move forward with this proposal because it will break all-the-things.

If you write documentation or a technical article and nobody understands it, you’ve done a crappy job. I should know; I’ve been writing this stuff for twenty years.

Extend, embrace, extinguish. And use lots of difficult words.

Patience is a virtue

As a reaction to web dev outcry Google temporarily halted the breaking of the web. That sounds great but really isn’t. It’s just a clever tactical move.

I saw this tactic in action before. Back in early 2016 Google tried to break the de-facto standard for the mobile visual viewport that I worked very hard to establish. I wrote a piece that resonated with web developers, whose complaints made Google abandon the plan — temporarily. They tried again in late 2017, and I again wrote an article, but this time around nobody cared and the changes took effect and backward compatibility was broken.

So the three ancient modals still have about 12 to 18 months to live. Somewhere in late 2022 to early 2023 Google will try again, web developers will be silent, and the modals will be gone.

The pursuit of appiness

But why is Google breaking the web forward at such a pace? And why is Apple holding it back?

Safari is kept dumb to protect the app store and thus revenue. In contrast, the Chrome team is pushing very hard to port every single app functionality to the browser. Ages ago I argued we should give up on this, but of course no one listened.

When performing Valley Kremlinology, it is useful to see Google policies as stemming from a conflict between internal pro-web and anti-web factions. We web developers mainly deal with the pro-web faction, the Chrome devrel and browser teams. On the other hand, the Android team is squarely in the anti-web camp.

When seen in this light the pro-web camp’s insistence on copying everything appy makes excellent sense: if they didn’t Chrome would lag behind apps and the Android anti-web camp would gain too much power. While I prefer the pro-web over the anti-web camp, I would even more prefer the web not to be a pawn in an internal Google power struggle. But it has come to that, no doubt about it.

Solutions?

Is there any good solution? Not really.

Jim Nielsen feels that part of the issue is the lack of representation of web developers in the standardization process. That sounds great but is proven not to work.

Three years ago Fronteers and I attempted to get web developers represented and were met with absolute disinterest. Nobody else cared even one shit, and the initiative sank like a stone.

So a hypothetical web dev representative in W3C is not going to work. Also, the organisational work would involve a lot of unpaid labour, and I, for one, am not willing to do it again. Neither is anyone else. So this is not the solution.

And what about Firefox? Well, what about it? Ten years ago it made a disastrous mistake by ignoring the mobile web for way too long, then it attempted an arrogant and uninformed come-back with Firefox OS that failed, and its history from that point on is one long slide into obscurity. That’s what you get with shitty management.

Pick your poison

So Safari is trying to slow the web down. With Google’s move-fast-break-absofuckinglutely-everything axiom in mind, is Safari’s approach so bad?

Regardless of where you feel the web should be on this spectrum between Google and Apple, there is a fundamental difference between the two.

We have the tools and procedures to manage Safari’s disinterest. They’re essentially the same as the ones we deployed against Microsoft back in the day — though a fundamental difference is that Microsoft was willing to talk while Apple remains its old haughty self, and its “devrels” aren’t actually allowed to do devrelly things such as managing relations with web developers. (Don’t blame them, by the way. If something would ever change they’re going to be our most valuable internal allies — just as the IE team was back in the day.)

On the other hand, we have no process for countering Google’s reverse embrace, extend, and extinguish strategy, since a section of web devs will be enthusiastic about whatever the newest API is. Also, Google devrels talk. And talk. And talk. And provide gigs of data that are hard to make sense of. And refer to their proprietary algorithms that “clearly” show X is in the best interest of the web — and don’t ask questions! And make everything so fucking complicated that we eventually give up and give in.

So pick your poison. Shall we push the web forward until it’s broken, or shall we break it by inaction? What will it be? Privately, my money is on Google. So we should say goodbye to the old web while we still can.

Syndicate content
©2003 - Present Akamai Design & Development.