Developer News

Painting With the Web

Css Tricks - Tue, 12/01/2020 - 10:47am

Matthias Ott, comparing how painter Gerhard Richter paints (do stuff, step back, take a look) to what can be the website building process and what can wreck it:

[…] this reminds me of designing and building for the Web: The unpredictability, the peculiarities of the material, the improvisation, the bugs, the happy accidents. There is one crucial difference, though. By using static wireframes and static layouts, by separating design and development, we are often limiting our ability to have that creative dialogue with the Web and its materials.

Love that. I’ve long thought that translating a mockup directly to code, while fun in its own way, is a left-brained task and doesn’t encourage as much creativity as either playing around in a design tool or playing around in the code while you build without something specific in mind as you do it. You don’t just, like, entirely re-position things and make big bold changes as much when your brain is in that mode of making something you see in one place (a mockup) manifest itself in another place (the code).

Direct Link to ArticlePermalink

The post Painting With the Web appeared first on CSS-Tricks.

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

A Microsite Showcasing Coding Fonts

Css Tricks - Tue, 12/01/2020 - 8:00am

We made one! It’s open source if you want to make it better or fix things.

There are quite a few purpose-built fonts for writing code. The point of this site is to show you some of the nicest options so you can be aware of them and perhaps pick one out to try that suites your taste.

We used screenshots of the code to display just so we could show off some of the paid fonts without managing a license just for this site, and for fonts without a clear way to link them up (like San. Also because setting up the screenshotting process was kinda fun.

High Fives

Special high five to Jonathan Land who helped a ton getting the site together including literally all the design work. Also to Sendil Kumar who had the original idea for a blog post like this, before the idea grew up into a full blown microsite. And finally to all the contributors so far.

Work

There are still more fonts to add. If you want to add one, feel free to make a PR. Or if you’re unsure if it will be accepted or not, open an issue first. I’d like to keep any of the fonts we add fairly high quality. There is also a current bug with some of the ligatures not showing properly in the screenshots of some of the fonts. I’m sure we’ll sort it out eventually, but I’d love an assist there if you are particularly knowledgeable in that area.

Open issues here.

The post A Microsite Showcasing Coding Fonts appeared first on CSS-Tricks.

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

How to Add Text in Borders Using Basic HTML Elements

Css Tricks - Tue, 12/01/2020 - 5:56am

Some HTML elements come with preset designs, like the inconveniently small squares of <input type="checkbox"> elements, the limited-color bars of <meter> elements, and the “something about them bothers me” arrows of the <details> elements. We can style them to match the modern aesthetics of our websites while making use of their functionalities. There are also many elements that rarely get used as both their default appearance and functionality are less needed in modern web designs.

One such HTML element is <fieldset>, along with its child element <legend>.

A <fieldset> element is traditionally used to group and access form controls. We can visually notice the grouping by the presence of a border around the grouped content on the screen. The caption for this group is given inside the <legend> element that’s added as the first child of the <fieldset>.

CodePen Embed Fallback

This combination of <fieldset> and <legend> creates a unique ready-made “text in border” design where the caption is placed right where the border is and the line of the border doesn’t go through the text. The border line “breaks” when it encounters the beginning of the caption text and resumes after the text ends.

In this post, we’ll make use of the <fieldset> and <legend> combo to create a more modern border text design that’s quick and easy to code and update.

CodePen Embed Fallback

For the four borders, we need four <fieldset> elements, each containing a <legend> element inside. We add the text that will appear at the borders inside the <legend> elements.

<fieldset><legend>Wash Your Hands</legend></fieldset> <fieldset><legend>Stay Apart</legend></fieldset> <fieldset><legend>Wear A Mask</legend></fieldset> <fieldset><legend>Stay Home</legend></fieldset>

To begin, we stack the <fieldset> elements on top of each other in a grid cell and give them borders. You can stack them using any way you want — it doesn’t necessarily have to be a grid.

Only the top border of each <fieldset> element is kept visible while the remaining edges are transparent since the text of the <legend> element appears at the top border of the <fieldset> by default.

Also, we give all the <fieldset> elements a box-sizing property with a value of border-box so the width and height of the <fieldset> elements include their border and padding sizes too. Doing this later creates a leveled design, when we style the <legend> elements.

body { display: grid; margin: auto; /* to center */ margin-top: calc(50vh - 170px); /* to center */ width: 300px; height: 300px; } fieldset { border: 10px solid transparent; border-top-color: black; box-sizing: border-box; grid-area: 1 / 1; /* first row, first column */ padding: 20px; width: inherit; }

After this, we rotate the last three <fieldset> elements in order to use their top borders as the side and bottom borders of our design.

/* rotate to right */ fieldset:nth-of-type(2){ transform: rotate(90deg); } /* rotate to bottom */ fieldset:nth-of-type(3){ transform: rotate(180deg); } /* rotate to left */ fieldset:nth-of-type(4){ transform: rotate(-90deg); }

Next up is styling the <legend> elements. The key to create smooth border text using a <legend> element is to give it a zero (or small enough) line-height. If it has a large line height, that will displace the position of the border it’s in, pushing the border down. And when the border moves with the line height, we won’t be able to connect all the four sides of our design and will need to readjust the borders.

legend { font: 15pt/0 'Averia Serif Libre'; margin: auto; /* to center */ padding: 0 4px; } fieldset:nth-of-type(3) > legend { transform: rotate(180deg); }

I used the font shorthand property to give the values for the font-size, line-height and font-family properties of the <legend> elements.

The <legend> element that adds the text at the bottom border of our design, fieldset:nth-of-type(3)>legend, is upside-down because of its rotated <fieldset> parent element. Flip that <legend> element vertically to show its text right-side-up.

Add an image to the first <fieldset> element and you get something like this:

CodePen Embed Fallback

Lateral margins can move the text along the border. Left and right margins with auto values will center the text, as seen in the above Pen. Only the left margin with an auto value will flush the text to the right, and vice versa, for the right margin.

CodePen Embed Fallback

Bonus: After a brief geometrical detour, here’s an octagonal design I made using the same technique:

CodePen Embed Fallback

The post How to Add Text in Borders Using Basic HTML Elements appeared first on CSS-Tricks.

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

Under-Engineered Responsive Tables

Css Tricks - Tue, 12/01/2020 - 5:56am

I first blogged about responsive data tables in 2011. When responsive web design was first becoming a thing, there were little hurdles like data tables that had to be jumped. The nature of <table> elements are that they have something a minimum width depending on the content they contain and that can easily exceed the width of a small screen device.

This image I made then still covers the issue pretty well:

Except… maybe they don’t equally suck. If that image on the left were scrollable, then maybe that’s actually… not so bad. In fact, that’s what I’ve done right here on CSS-Tricks recently. I think it’s the safest way of handling responsive tables when you have no idea what content the table contains. That’s the case here, where I need to set up base table styles that apply to any blog post which may contain a table.

The crux of the idea of a scrollable table is to wrap it in a <div> that has overflow: auto; on it. That way the <table> inside is free to exceed the width of the parent, but it won’t “blow out the width” and instead triggers a scrollbar. This isn’t quite enough though, so here’s Adrian Roselli with the real scoop. The wrapping <div> needs to be focusable and labelled, so:

<div role="region" aria-labelledby="Caption01" tabindex="0"> <table> <caption id="Caption01">Appropriate caption</caption> <!-- ... --> </table> </div>

Then apply the scrolling and focus styles, in the condition you’ve done everything else right:

[role="region"][aria-labelledby][tabindex] { overflow: auto; } [role="region"][aria-labelledby][tabindex]:focus { outline: .1em solid rgba(0,0,0,.1); }

If you’re going to further engineer responsive tables, there are all sorts of options. One of the classics is to display: block a lot of the elements, meaning that all the data in a row (<tr>) ends up as a chunk of stacked content together that stands less of a chance of breaking the parent element’s width. You can get all the data labels properly with pseudo-elements. But, this only makes sense when individual rows of content make perfect sense alone. That’s not the case with every table. A table’s purpose might be cross-referencing data, and in that case, you’ve ruined that with this approach. So again, there are nice approaches for responsive tables when you know exactly the content and purpose of the table. But the best responsive solution when you don’t know is to just make sure they are swipeable.

Direct Link to ArticlePermalink

The post Under-Engineered Responsive Tables appeared first on CSS-Tricks.

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

Native CSS Masonry Layout In CSS Grid

Css Tricks - Mon, 11/30/2020 - 2:45pm

Rachel Andrew introducing the fact that masonry layout is going to be a thing in native CSS via CSS grid layout. The thing with masonry is that we can already do it for the most part, but there is just one thing that makes it hard: doing the vertical-staggering and having a left-to-right source order. So that’s what this new ability will solve in addition to it just being less hacky in general.

You can already test a partial implementation in Firefox Nightly by enabling layout.css.grid-template-masonry-value.enabled.

.container { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: masonry; }

I like the grid-template-rows: masonry; syntax because I think it clearly communicates: “You aren’t setting these rows. In fact, there aren’t even really rows at all anymore, we’ll take care of that.” Which I guess means there are no rows to inherit in subgrid, which also makes sense.

The post Native CSS Masonry Layout In CSS Grid appeared first on CSS-Tricks.

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

Super Tiny Icons

Css Tricks - Mon, 11/30/2020 - 9:35am

A bunch of SVG icons (of popular things) all under 1KB. SVG is awesome for golfing.

I was going to add a CodePen logo but there is already one in there at 375 Bytes. I’ve got one at 208 Bytes, based on a logo update David DeSandro did for us a couple years back.

Direct Link to ArticlePermalink

The post Super Tiny Icons appeared first on CSS-Tricks.

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

A Complete State Machine Made With HTML Checkboxes and CSS

Css Tricks - Fri, 11/27/2020 - 5:54am

State machines are typically expressed on the web in JavaScript and often through the popular XState library. But the concept of a state machine is adaptable to just about any language, including, amazingly, HTML and CSS. In this article, we’re going to do exactly that. I recently built a website that included a “no client JavaScript” constraint and I needed one particular unique interactive feature.

The key to all this is using <form> and <input type="radio"> elements to hold a state. That state is toggled or reset with another radio <input> or reset <button> that can be anywhere on the page because it is connected to the same <form> tag. I call this combination a radio reset controller, and it is explained in more detail at the end of the article. You can add more complex state with additional form/input pairs.

It’s a little bit like the Checkbox Hack in that, ultimately, the :checked selector in CSS will be doing the UI work, but this is logically more advanced. I end up using a templating language (Nunjucks) in this article to keep it manageable and configurable.

Traffic light state machine

Any state machine explanation must include the obligatory traffic light example. Below is a working traffic light that uses a state machine in HTML and CSS. Clicking “Next” advances the state. The code in this Pen is post processed from the state machine template to fit in a Pen. We’ll get into the code in a more readable fashion later on.

CodePen Embed Fallback Hiding/Showing table information

Traffic lights aren’t the most practical every-day UI. How about a <table> instead?

There are two states (A and B) that are changed from two different places in the design that affect changes all over the UI. This is possible because the empty <form> elements and <input> elements that hold state are at the very top of the markup and thus their state can be deduced with general sibling selectors and the rest of the UI can be reached with descendent selectors. There is a loose coupling of UI and markup here, meaning we can change the state of almost anything on the page from anywhere on the page.

CodePen Embed Fallback General four-state component Diagram of a generic four-state finite state machine

The goal is a general purpose component to control the desired state of the page. “Page state” here refers to the desired state of the page and “machine state” refers to the internal state of the controller itself. The diagram above shows this generic state machine with four states(A, B, C and D). The full controller state machine for this is shown below. It is built using three of the radio reset controller bits. Adding three of these together forms a state machine that has eight internal machine states (three independent radio buttons that are either on or off).

Diagram of the controller’s internal states

The “machine states” are written as a combination of the three radio buttons (i.e. M001 or M101). To transition from the initial M111 to M011, the radio button for that bit is unset by clicking on another radio <input> in the same group. To transition back, the reset <button> for the <form> attached to that bit is clicked which restores the default checked state. Although this machine has eight total states, only certain transitions are possible. For instance, there is no way to go directly from M111 to M100 because it requires two bits to be flipped. But if we fold these eight states into four states so that each page state shares two machine states (i.e. A shares states M111 and M000) then there is a single transition from any page state to any other page state.

Reusable four-state component

For reusability, the component is built with Nunjucks template macros. This allows it to be dropped into any page to add a state machine with the desired valid states and transitions. There are four required sub-components:

  • Controller
  • CSS logic
  • Transition controls
  • State classes
Controller

The controller is built with three empty form tags and three radio buttons. Each of the radio buttons checked attribute is checked by default. Each button is connected to one of the forms and they are independent of each other with their own radio group name. These inputs are hidden with display: none because they are are not directly changed or seen. The states of these three inputs comprise the machine state and this controller is placed at the top of the page.

{% macro FSM4S_controller()%} <form id="rrc-form-Bx00"></form> <form id="rrc-form-B0x0"></form> <form id="rrc-form-B00x"></form> <input data-rrc="Bx00" form="rrc-form-Bx00" style="display:none" type="radio" name="rrc-Bx00" checked="checked" /> <input data-rrc="B0x0" form="rrc-form-B0x0" style="display:none" type="radio" name="rrc-B0x0" checked="checked" /> <input data-rrc="B00x" form="rrc-form-B00x" style="display:none" type="radio" name="rrc-B00x" checked="checked" /> {% endmacro %} CSS logic

The logic that connects the controller above to the state of the page is written in CSS. The Checkbox Hack uses a similar technique to control sibling or descendant elements with a checkbox. The difference here is that the button controlling the state is not tightly coupled to the element it is selecting. The logic below selects based on the “checked” state of each of the three controller radio buttons and any descendant element with class .M000. This state machine hides any element with the .M000 class by setting display: none !important. The !important isn’t a vital part of the logic here and could be removed; it just prioritizes the hiding from being overridden by other CSS.

{%macro FSM4S_css()%} <style> /* Hide M000 (A1) */ input[data-rrc="Bx00"]:not(:checked)~input[data-rrc="B0x0"]:not(:checked)~input[data-rrc="B00x"]:not(:checked)~* .M000 { display: none !important; } /* one section for each of 8 Machine States */ </style> {%endmacro%} Transition control

Changing the state of the page requires a click or keystroke from the user. To change a single bit of the machine state, the user clicks on a radio button that is connected to the same form and radio group of one of the bits in the controller. To reset it, the user clicks on a reset button for the form connected to that same radio button. The radio button or the reset button is only shown depending on which state they are in. A transition macro for any valid transition is added to the HTML. There can be multiple transitions placed anywhere on the page. All transitions for states currently inactive will be hidden.

{%macro AtoB(text="B",class="", classBtn="",classLbl="",classInp="")%} <label class=" {{class}} {{classLbl}} {{showM111_A()}} "><input class=" {{classInp}} " form="rrc-form-Bx00" type="radio" name="rrc-Bx00" />{{text}}</label> <button class=" {{class}} {{classBtn}} {{showM000_A1()}} " type="reset" form="rrc-form-Bx00">{{text}}</button> {%endmacro%} State class

The three components above are sufficient. Any element that depends on state should have the classes applied to hide it during other states. This gets messy. The following macros are used to simplify that process. If a given element should be shown only in state A, the {{showA()}} macro adds the states to hide.

{%macro showA() %} M001 M010 M100 M101 M110 M011 {%endmacro%} Putting it all together

The markup for the traffic light example is shown below. The template macros are imported in the first line of the file. The CSS logic is added to the head and the controller is at the top of the body. The state classes are on each of the lights of the .traffic-light element. The lit signal has a {{showA()}} macro while the “off” version of signal has the machine states for the .M000 and .M111 classes to hide it in the A state. The state transition button is at the bottom of the page.

{% import "rrc.njk" as rrc %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Traffic Light State Machine Example</title> <link rel="stylesheet" href="styles/index.processed.css"> {{rrc.FSM4S_css()}} </head> <body> {{rrc.FSM4S_controller()}} <div> <div class="traffic-light"> <div class="{{rrc.showA()}} light red-light on"></div> <div class="M111 M000 light red-light off"></div> <div class="{{rrc.showB()}} light yellow-light on"></div> <div class="M100 M011 light yellow-light off"></div> <div class="{{rrc.showC()}} light green-light on"></div> <div class="M010 M101 light green-light off"></div> </div> <div> <div class="next-state"> {{rrc.AtoC(text="NEXT", classInp="control-input", classLbl="control-label",classBtn="control-button")}} {{rrc.CtoB(text="NEXT", classInp="control-input", classLbl="control-label",classBtn="control-button")}} {{rrc.BtoA(text="NEXT", classInp="control-input", classLbl="control-label",classBtn="control-button")}} </div> </div> </div> </body> </html> View Demos Extending to more states

The state machine component here includes up to four states which is sufficient for many use cases, especially since it’s possible to use multiple independent state machines on one page.

That said, this technique can be used to build a state machine with more than four states. The table below shows how many page states can be built by adding additional bits. Notice that an even number of bits does not collapse efficiently, which is why three and four bits are both limited to four page states.

Bits (rrcs)Machine statesPage states12224238441645326 Radio reset controller details

The trick to being able to show, hide, or control an HTML element anywhere on the page without JavaScript is what I call a radio reset controller. With three tags and one line of CSS, the controlling button and controlled element can be placed anywhere after this controller. The controlled side uses a hidden radio button that is checked by default. That radio button is connected to an empty <form> element by an ID. That form has a type="reset" button and another radio input that together make up the controller.

<!-- RRC Controller --> <form id="rrc-form"></form> <label> Show <input form="rrc-form" type="radio" name="rrc-group" /> </label> <button type="reset" form="rrc-form">Hide</button> <!-- Controlled by RRC --> <input form="rrc-form" class="hidden" type="radio" name="rrc-group" checked /> <div class="controlled-rrc">Controlled from anywhere</div>

This shows a minimal implementation. The hidden radio button and the div it controls need to be siblings, but that input is hidden and never needs to be directly interacted with by the user. It is set by a default checked value, cleared by the other radio button, and reset by the form reset button.

input[name='rrc-group']:checked + .controlled-rrc { display: none; } .hidden { display: none; }

Only two line of CSS are required to make this work. The :checked pseudo selector connects the hidden input to the sibling it is controlling. It adds the radio input and reset button that can be styled as a single toggle, which is shown in the following Pen:

CodePen Embed Fallback Accessibility… should you do this?

This pattern works, but I am not suggesting it should be used everywhere for everything. In most cases, JavaScript is the right way to add interactivity to the web. I realize that posting this might get some heat from accessibility and semantic markup experts. I am not an accessibility expert, and implementing this pattern may create problems. Or it may not. A properly labelled button that does something to the page controlled by otherwise-hidden inputs might work out fine. Like anything else in accessibility land: testing is required.

Also, I have not seen anyone else write about how to do this and I think the knowledge is useful — even if it is only appropriate in rare or edge-case situations.

GitHub Repo

The post A Complete State Machine Made With HTML Checkboxes and CSS appeared first on CSS-Tricks.

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

Exploring What the Details and Summary Elements Can Do

Css Tricks - Thu, 11/26/2020 - 5:11am

Gosh bless the

element. Toss some content inside it and you have an accessible expand-for-more interaction with just about zero work.

See the Pen Simple details. by Chris Coyier (@chriscoyier) on CodePen.

Toss a

in there to customize what the expander text says.

See the Pen Multiple Details/Summary by Chris Coyier (@chriscoyier) on CodePen.

Works great for FAQs.

There is really no limit to how you can style them. If you don’t like the default focus ring, you can remove that, but make sure to put some kind of styling back.

Here I’ve used a header element for each expandable section, which has a focus state that mimics other interactive elements on the page.

The only browser that doesn’t support this are the Microsoft ones (and Opera Mini which makes sense—it doesn’t really do interactive).

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari1249No796Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari878346.0-6.1

But even then, it’s just like all the sections are opened, so it’s not a huge deal:

Wanna style that default triangle? Strangely enough, the standard way to do that is through the list-style properties. But Blink-based browsers haven’t caught up to that yet, so they have a proprietary way to do it. They can be combined though. Here’s an example of replacing it with an image:

summary { list-style-image: url(right-arrow.svg); } summary::-webkit-details-marker { background: url(right-arrow.svg); color: transparent; }

See the Pen Custom Markers on Details/Summary by Chris Coyier (@chriscoyier) on CodePen.

Unfortunately, they don’t turn, and there is no way to animate the default triangle either. One idea might be to target the :focus state and swap backgrounds:

See the Pen Custom Markers on Details/Summary by Geoff Graham (@geoffgraham) on CodePen.

But that seems to be limited to WebKit and Blink and, even then, the arrow will return once the item is out of focus even if the item is still expanded.

The post Exploring What the Details and Summary Elements Can Do appeared first on CSS-Tricks.

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

Three Things You Didn’t Know About AVIF

Css Tricks - Thu, 11/26/2020 - 5:07am

AVIF, the file format based on the AV1 video codec, is the latest addition to the next-gen image formats. Early reports and comparisons show good results compared to JPEG and WebP. However, even if browser support is good, AVIF is still on the bleeding edge in regards to encoding and decoding. Encoding, decoding, settings and parameters has been well discussed elsewhere. 

No doubt, AVIF images generate a smaller payload and are nice looking. In this post, we’ll take a closer look at issues to be aware or before you go all in on AVIF.

1. WebP is Better for Thumbnails

One interesting observation is that for small dimension images, WebP will produce lighter payload images than AVIF.

It’s probably possible to explain why, and tune the encoder to address this case. However, that is not an option for most people. Most people would probably rely on an image optimizer like squoosh.app or an image CDN like ImageEngine. The below comparison uses exactly these two alternatives for AVIF conversion. 

We see that WebP will generally produce images with a higher file size than AVIF. On larger dimension images, ImageEngine performs significantly better than squoosh.app.

Now, to the interesting observation. On images around 100px × 100px squoosh.app passes ImageEngine on effectiveness, but then also WebP catches up and for a 80px x 80px image. WebP is actually the most effective image measured in file size. 

The test performs relatively consistently on different types of images. For this illustration, this image from Picsum is used.

PixelsOriginal JPEG (bytes)Optimized WebP (bytes)ImageEngine AVIF (bytes)squoosh.app AVIF (bytes)501,475598888687802,0901,0761,2341,0701103,0221,7161,5921,5801504,4572,8082,1532,2751705,3003,2242,4502,6702307,7924,8863,1893,90029010,8956,7744,0565,130 2. AVIF Might Not Be the Best for Product Images with High Entropy

Typically, a product page consists of images of the product, and when a user’s mouse hovers over or clicks on the product image, it zooms in to offer a closer look at the details.

It is worth noting that AVIF will in certain cases reduce the level of detail, or perceived sharpness, of the image when zoomed in. Especially on a typical product image where the background is blurred or has low entropy while foreground, and product, has more detail and possibly higher entropy.

Below is a zoomed in portion of a bigger product image (JPEG, AVIF) which clearly illustrates the difference between a regularly optimized JPEG versus an AVIF image optimized by squoosh.app.


The AVIF is indeed much lighter than the JPEG, but in this case the trade off between visual quality and lower file size has gone too far. This effect will not be as perceptible for all types of images, and therefore will be difficult to proactively troubleshoot in an automated build process that relies on responsive images syntax for format selection.

Moreover, unlike JPEG, AVIF does not support progressive rendering. For a typical product detail page, progressive rendering might provide a killer feature to improve key metrics like Largest Contentful Paint and other Core Web Vitals metrics. Even if a JPEG takes a little bit longer time to download due to its larger file size compared to AVIF, chances are that it will start rendering sooner than an AVIF thanks to its progressive rendering mechanism. This case is well illustrated by this video from Jake Achibald.

3. JPEG 2000 is Giving AVIF Tough Competition

The key selling point of AVIF is its extremely low file size relative to an acceptable visual image quality. Early blogs and reports have been focusing on this. However, JPEG2000 (or JP2) may in some cases be a better tool for the job. JPEG2000 is a relatively old file format and does not get the same level of attention as AVIF, even if the Apple side of the universe already supports JPEG2000.

To illustrate, let’s look at this adorable puppy. The AVIF file size optimized by squoosh.app is 27.9 KB with default settings. Converting the image to JPEG2000, again using ImageEngine, the file size is 26.7 KB. Not much difference, but enough to illustrate the case.

What about the visual quality? DSSIM is a popular way to compare how visually similar an image is to the original image. The DSSIM metric compares the original image to a converted file, with a lower value indicating better quality. Losslessly converting the AVIF and JPEG2000 version to PNG, the DSSIM score is like this:

DSSIM (0 = equal to original)BytesJPEG20000.01926.7 KBAVIF0.01227.9 KB

AVIF has slightly better DSSIM but hardly visible to the human eye.

Right Tool for the Job

The key takeaway from this article is that AVIF is hardly the “silver bullet,” or the one image format to rule them all. First of all, it is still very early in the development of both encoders and decoders. In addition, AVIF is yet another format to manage. Like Jake Archibald also concludes in his article, offering 3+ versions of each image on your webpage is a bit of a pain unless the entire workflow (resize, compress, convert, select, deliver) is all automated.

Also, like we’ve seen, just because a browser supports AVIF, it doesn’t mean that it is the best choice for your users.

Using responsive images and adding AVIF to the list of image formats to pre-create is better than not considering AVIF at all. A potential challenge is that the browser will then pick AVIF if it’s supported regardless of whether AVIF is the right tool or not.

However, using an image CDN like ImageEngine, will to a greater extent be able to dynamically choose between supported formats and make a qualified guess whether WEBP, JPEG2000 or AVIF will give the best user experience. Using an image CDN to automate the image optimization process will take into account browser compatibility, image payload size and visual quality.

The post Three Things You Didn’t Know About AVIF appeared first on CSS-Tricks.

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

Tailwind versus BEM

Css Tricks - Wed, 11/25/2020 - 11:40am

Some really refreshing technological comparison writing from Eric Bailey. Like, ya know, everything in life, we don’t have to hate or love everything. Baby bear thinking, I like to say. There are benefits and drawbacks. Every single bullet point here is well-considered and valid. I really like the first in each section, so I’ll quote those as a taste here:

Tailwind Benefit: “The utility CSS approach creates an API-style approach to thinking about CSS, which helps many developers work with it.”

Tailwind Drawback: “You need to learn Tailwind class names in addition to learning CSS property names to figure out the visual styling you want. Tailwind is reliant on, and will be outlived by CSS, so it is more long-term beneficial to focus on CSS’ capabilities directly.”

BEM Benefit: “BEM will allow you to describe any user interface component you can dream up in a flexible, extensible way. As it is an approach to encapsulate the full range of CSS properties, it will allow you to style things Tailwind simply does not have classes for—think highly art directed experiences.”

BEM Drawback: “BEM runs full-tilt into one of the hardest problems in computer science—naming things. You need to not only describe your component, but also all its constituent parts and their states.”

And remember, these certainly aren’t the only two choices on the block. I covered my thoughts on some other approaches here.

Direct Link to ArticlePermalink

The post Tailwind versus BEM appeared first on CSS-Tricks.

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

Considerations for Making a CSS Framework

Css Tricks - Wed, 11/25/2020 - 5:47am

Around eight months ago, I started building a framework which would eventually go on to become Halfmoon. I made a post on this very website announcing the launch of the very first version. Halfmoon has been billed as a Bootstrap alternative with a built-in dark mode feature, that is especially good when it comes to building dashboards and tools. All of this still applies to the framework.

However, today I would like to talk about an area of the framework that is a bit understated. I believe our industry as a whole seriously underestimates the value of customization and user personalization, i.e. users being able to set their own design preferences. Chris has written before about knowing who a design system is made for, pointing out a spectrum of flexibility depending on who a system is meant to help.

But it’s more than design systems. Let’s talk about how Halfmoon addresses these issues because they’re important considerations for knowing which framework works best for your specific needs.

Dashboard built using Halfmoon Who is Halfmoon for?

Before diving in, let’s address an important question: Is Halfmoon the right framework for you? Here’s a list of questions to help you answer that:

  • Are you building a dashboard, tool, or even a documentation website? Halfmoon has many unique components and features that are specific to these use cases.
  • Are you familiar with Bootstrap’s class names, but wish that the design was a bit more premium-looking?
  • Does your users want or expect a dark mode on your website?
  • Do you dislike dependencies? Halfmoon does not use jQuery, and also has no build process involving CSS preprocessors. Everything is pure, vanilla CSS and JavaScript.
  • Are you tired of dealing with complex build systems and front-end tooling? This ties in to the previous point. Personally, I find it difficult to deal with front-end tooling and build processes. As mentioned above, Halfmoon has no build process, so you just pull in the files (local, CDN, or npm), and start building.

If you answered yes to any (or all) of these questions, you should probably give Halfmoon a try. It is important to note however, that Halfmoon is not a UI component library for React/Vue/Angular, so you shouldn’t go into it expecting that. Moreover, if you are more fond of purely utility driven development, then Tailwind CSS is a better option for you. When it comes to CSS utilities, Halfmoon takes a middle of the road approach – there are utilities plus semantic classes for common components.

Using CSS custom properties

First, let’s get the easy stuff out of the way. CSS custom properties are incredible, and I expect them to completely replace preprocessor variables in the future. Browser support is already at a solid ~96%, and with Internet Explorer being phased out by Microsoft, they are expected to become a standard feature.

Halfmoon is built entirely using CSS variables because they provide a huge degree of customization. Now, you might immediately think that all this means is that there are a few custom properties for colors sprinkled in there, but it’s more than that. In fact, there are over 1,500 global variables in Halfmoon. Almost everything can be customized by overriding a property. Here’s a nifty example from the docs:

Swapping out a few custom property values opens up a ton of possibilities in Halfmoon, whether it’s theming things for a brand, or tweaking the UI to get just the right look.

That’s what we’re talking about here when it comes to customization: does the system still stand up and work well if the person using it overrides anything. I have written extensively about this (and much more) in the official Halfmoon docs page.

Variables aren’t a new concept to frameworks. Many frameworks actually use Sass or Less variables and have done so for quite a while. That’s still a good and effective way to establish a customizable experience. But at the same time, those will lock into a preprocessor (which, again, doesn’t have to be a bad thing). By relying instead on CSS custom properties — and variable-izing all the things — we are relying on native CSS, and that doesn’t require any sort of build dependency. So, not only can custom properties make it easier to customize a framework, but they are much more flexible in terms of the tech stack being used.

There is a balance to be had. I know I suggested creating variables for everything, but it can be equally tough to manage and maintain scores and scores of variables (just like anything else in the codebase). So, lean heavily on variables to make a framework or design system more flexible, but also be mindful of how much flexibility you need to provide and whether adding another variable is part of that scope.

Deciding what components to include

When it comes to building a CSS framework, deciding what components to include is a big part of that ordeal. Of course, for a developer working on a passion project, you want to include everything. But that is simply not feasible, so a few decisions were made on my part.

As of right now, Halfmoon has most of the components you can find in similar frameworks such as Bootstrap or Bulma. These frameworks are great and widely used, so they are a good frame of reference. However, as I have mentioned already, a unique thing about Halfmoon is the focus on building tools and dashboards on the web. This niche, if you could call it that, has led me to build some unique components and features:

  • 5 different types of sidebars, with built-in toggle and overlay handlers. Sidebars are very important for most dashboards and tools (and a pain to get right), so this was a no brainer.
  • 2 different types of navbars. There is one that sticks to the bottom of the page, which can be used to great effect for action buttons. Think about the actions that pop up when you select items on data-table. You could place those action buttons here.
  • Omni-directional dropdowns (with 12 different placements, 3 for each direction).
  • Beautiful form components.
  • Built-in keyboard shortcut system, with an easy way to declare new ones for your tool.
  • Tons of utilities. Of course, this is not comparable to Tailwind CSS, but Halfmoon has enough responsive utility classes to handle a lot of use cases right out of the box.

Moreover, the built-in dark mode, huge customizability, and the standard look and feel to the components, should all work together to make Halfmoon a great tool for building web tools and dashboards. And I am hopefully nowhere close to being done! The next updates will bring in a form validator (demo video), more form components, multi-select component, date and time picker, data-table component, etc.

So what is exactly missing from Halfmoon? Well the most obvious ones are tabs, list group, and spinners. But all of these are planned to be added in v1.2.0, which is the next update. There are also other missing components such as carousels, tree navigation, avatars, etc, which are slightly out of scope.

Providing user preferences

Giving end users the ability to set their preferences is often overlooked by frameworks. Things like setting the font size of an article, or whether to use a dark or light theme. In some ways, it’s sort of funny, because the web is catching up to what operating systems have allowed users to do for decades.

Here are some examples of user personalization on the web:

  1. Being able to select your preferred color mode. And, even better, the website automatically saves and respects your preference when the page is loaded. Or better yet, looking at your operating system preferences and automatically accommodating them.
  2. Setting the default size of elements. Especially font size. A small font might look good in a design, but allowing users to set their ideal font size makes the content actually readable. Technically, every modern browser has an option to zoom into content, but that is often unwieldy, and does not actually save your settings.
  3. Setting the compactness of elements. For example, some people prefer large padding with rounded corners, while others find it a waste of space, instead preferring a tighter UI. Sort of like how Gmail lets you decide whether you want a lot of breathing room in your inbox or make it as small and tight as possible to see more content.
  4. Setting the primary color on the website. While this is entirely cosmetic, it is still charming to be able to set your favorite color on every button and link on a website.
  5. Enabling a high contrast mode. Someone pointed this out to me on GitHub. Apparently, many (and I mean many) CSS frameworks often fail the minimum contrast recommended between foreground and background colors on common elements, such as buttons. That list includes Halfmoon. This is often a tradeoff, because overly contrastive elements often look worse (purely in terms of aesthetic). User personalization can allow you to turn on a high contrast mode, if you have difficulty with the default contrast.

Allowing for user personalizations can be really difficult to pull off — especially for a framework — because that would could mean swapping out huge parts of CSS to accommodate the different personalization settings and combinations. However, with a framework like Halfmoon (i.e. built entirely using CSS variables), this becomes trivial as CSS variables can be set and changed on run-time using JavaScript, like so:

// Get the <html> tag (for reading and setting variables in global scope) var myElement = document.documentElement; // Read CSS variable getComputedStyle(myElement).getPropertyValue("--variable-name"); // Set CSS variable myElement.style.setProperty("--variable-name", "value");

Therefore, user personalization can be implemented using Halfmoon in the following way:

  • The user sets a preference. That basically means a variable value gets changed. The variable is set with JavaScript (as shown above), and the new value is stored in a cookie or local storage.
  • When the user comes back to the website, their preferences are retrieved and set using JavaScript (again, as shown above) once the page is loaded.

Here are visual examples to really hammer the point home.

Setting and saving the default font size

In the example above, whenever the range slider is changed, the variable --base-font-size is updated to the slider’s value. This is great for people who prefer larger text. As explained in the previous section, this new value can be saved in a cookie or local storage, and the next time the user visits the website, the user preference can be set on page load.

Setting the compactness of content Because there are CSS custom properties used as utilities, like spacing and borders, we can remove or override them easily to create a more compact or expanded component layout.

Only two variables are updated in this example to go from an expanded view to a compact one:

  • --content-and-card-spacing changed from 3rem (30px) to 2rem (20px).
  • --card-border-radius changed from 0.4rem (4px) to 0.2rem (2px).

For a real life scenario, you could have a dropdown that asks the user whether they prefer their content to be Default or Compact, and choosing one would obviously set the above CSS variables to theme the site. Once again, this could be saved and set on page load when the user visits the website on their next session.

Wait, but why?

Even with all the examples I have shown so far, you may still be asking why is this actually necessary. The answer is really simple: one size does not fit all. In my estimate, around half of the population prefers a dark UI, while the other half prefers light. Similarly, people have wild variations about the things they like when it comes to design. User personalization is a form of improving the UX, because it lets the user choose what they prefer. This may not be so important on a landing page, but when it comes to a tool or dashboard (that one has to use for a long time to get something done), having a UI that can be personalized is a boon to productivity. And knowing that is what Halfmoon is designed to do makes it ideal for these types of use cases.

Moreover, you know how people often complain that websites made with a certain framework (eg Bootstrap) all look the same? This is a step toward making sure that websites built with Halfmoon will always look distinct, so that the focus is on the website and content itself, and not on the framework that was used to build it.

Again, I am not saying that everything should be allowed to be personalized. But knowing who the framework is for and what it is designed to do helps make it clear what should be personalized.

Looking ahead

I strongly feel that flexibility for customization and accounting for user preferences are often overlooked on the web, especially in the framework landscape. That’s what I’m trying to address with Halfmoon.

In the future, I want to make it a lot easier for developers to implement user preferences, and also promote diversity of design with new templates and themes. That said, here are some things on the horizon for Halfmoon:

  • A form validator (demo video)
  • New components, including range sliders, tabs and spinners
  • High contrast mode user preference
  • Multi-select component (like Select2, only without jQuery)
  • A date and time picker
  • A data-table component
  • A GUI-based form builder
  • More themes and templates

You can, of course, learn more about Halfmoon in the documentation website, and if you want to follow the project, you can give it a star on GitHub.

The post Considerations for Making a CSS Framework appeared first on CSS-Tricks.

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

Rendering Spectrum

Css Tricks - Tue, 11/24/2020 - 2:30pm

Here are the big categories of rendering websites:

  • Client: ship a <div id="root"></div> and let a JavaScript template render all of it.
  • Static: pre-render the HTML.
  • Server: let a live server process requests and generate the HTML response.

They are not mutually exclusive.

  • A website could statically pre-render 75% of it’s pages (say, blog posts), but the other 25% have a server respond to (say, forums).
  • A website could statically pre-render all the pages, but have a couple of empty <div>s in there that have client-side rendered content in them (e.g. a dynamically generated menu based on the logged-in user).
  • A website could be primarily server-rendered, but has caching in front of it such that it behaves statically.
  • A website could render statically, but then “hydrate” itself into an entirely client-rendered site.
  • A website could be a mix of server and static rendering, but have dynamic parts similar to client-side rendering, but actually happen in an edge function, so it ends up more like server-side rendering.

Next.js is interesting in that it can do all three. Here’s Tim Neutkens in a recent interview:

Next.js allows you to pre-render pages. It creates HTML on a server at build time with static site generation or uses run-time rendering on the server side. Next allows you to do a hybrid of those. Unlike most other frameworks, you are not bound by, oh, I’m going to build my app completely statically generated. Instead, you’re allowed to have some pages be server-side rendered and some pages be statically generated.

In the new release we make it possible to update these statically generated pages without having to run a new build, rebuilding your whole app.

Cool. Love to see that happening at the framework level. Seems like having to go all-in on one rendering style isn’t practical for a lot of sites.

Client rendering is the most flexible, but comes with all these serious downsides like worse performance, worse reliability, more strain on devices, bad SEO, etc. Static pre-rendering is the most robust, speedy, and secure, but is the most limited. Edge functions on top of static is starting to open doors, but server-rendering is the classic mix of flexibility and speed that has dominated the web for good reason.

Client rendering also opens the door for that “SPA” (Single Page App) feel. I still like that, personally. I like the no-page-refresh feel. It’s makes a site feel snappy and opens the door for page transitions. Gatsby is famous for popularizing hydration, where you get the pre-rendered static bonus, but then the upgrade into SPA as the JavaScript downloads.

I’d love to see the web get to the point where we get all that “good feel” bonus of an SPA without actually having to build an SPA. It’s notable when frameworks provide SPA feels without having to manage much of that yourself, but still, something is managing it and that something is a bunch of JavaScript.

Tom MacWright wrote about that recently in his “If not SPAs, What?” post. Some of today’s alternatives:

Turbolinks … what is the bare minimum you need to do to get the SPA experience without any cooperation from your application?

Turbolinks is like… click link, click is intercepted, Ajax request for new page is made, JavaScript flops out the content on the page with the new content. Super easy to implement, but it’s still JavaScript, and not particularly intelligent about sending less data across the wire.

barba.js and instant.page are alternative approaches to the same sort of problem.

Barba is all about getting page transitions going (more detail on that concept). instant.page is all about pre-loading/rendering pages right before you click then, so even though you get a page refresh, it feels less intrusive (particularly with paint holding). Both are cool, but not quite one-to-one replacements for an SPA. (Even with paint holding, pre-rendering, and lightweight pages, I still don’t think the experience is quite a smooth as an SPA. For example, you still get the page loading spinner.)

So… is the anything else cooking? Kinda. There is <portal>. Possibly too simplified, but here goes: portals are like iframes. They can even be visually displayed just like an iframe. That means the rendering of the URL in the portal is already done. Then you can “promote” the portal to be the active page, and even animate the portal itself while doing so.

I don’t hate it. I can imagine someone building a turbolinks-like library on portals so they are “easy to use” and make a site more SPA-like.

Still, animating a rectangle into position isn’t often what is desired from animated page transitions. Just look at Sarah’s “Native-Like Animations for Page Transitions on the Web” article. That’s what the people want (at least the possibility of it). That’s why Jeremy said not portals the other day when he cheekily said that “[m]ost single page apps are just giant carousels.” He also points to Jake’s navigation-transitions proposal from a few years back.

I love this proposal. It focuses on user needs. It also asks why people reach for JavaScript frameworks instead of using what browsers provide. People reach for JavaScript frameworks because browsers don’t yet provide some functionality: components like tabs or accordions; DOM diffing; control over styling complex form elements; navigation transitions. The problems that JavaScript frameworks are solving today should be seen as the R&D departments for web standards of tomorrow. (And conversely, I strongly believe that the aim of any good JavaScript framework should be to make itself redundant.)

So what’s the best rendering method? Whatever works best for you, but perhaps a hierarchy like this makes some general sense:

  1. Static HTML as much as you can
  2. Edge functions over static HTML so you can do whatever dynamic things
  3. Server generated HTML what you have to after that
  4. Client-side render only what you absolutely have to

The post Rendering Spectrum appeared first on CSS-Tricks.

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

The Core Web Vitals hype train

Css Tricks - Tue, 11/24/2020 - 5:42am

Some baby bear thinking from Katie Sylor-Miller:

my excitement for Core Web Vitals is tempered with a healthy skepticism. I’m not yet convinced that Largest Contentful Paint (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS) are the right metrics that all sites should be measuring themselves against. I worry that the outsized emphasis placed on Core Web Vitals by including them in SEO scoring will result in developers focusing solely on those three numbers without truly understanding what they mean and, more importantly, what they don’t mean.

Katie is pro-Web Core Vitals because of their correlation with real user experiences, but there is a lot more to think about. If we focus only on these metrics (because we have now an extremely strong incentive to do so) we’re missing out on a lot. They may not be the analytics that matter most to us or that correlate with business goals.

The horse’s mouth says the SEO implications don’t start until May 2021.

I admit I’ve been on the hype train myself a little bit. I like all of Katie’s points but I think I’ll still call it a step forward for web analytics. Robin also mentioned Sentry could do the tracking the other day.

Jeremy mentions the proliferation of initials:

Personally, my beef with core web vitals is that they introduce even more uneccessary initialisms (see, for example, Harry’s recent post where he uses CWV metrics like LCP, FID, and CLS—alongside TTFB and SI—to look at PLPs, PDPs, and SRPs. I mean, WTF?).

Direct Link to ArticlePermalink

The post The Core Web Vitals hype train appeared first on CSS-Tricks.

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

Creating UI Components in SVG

Css Tricks - Mon, 11/23/2020 - 1:35pm

I’m thoroughly convinced that SVG unlocks a whole entire world of building interfaces on the web. It might seem daunting to learn SVG at first, but you have a spec that was designed to create shapes and yet, still has elements, like text, links, and aria labels available to you. You can accomplish some of the same effects in CSS, but it’s a little more particular to get positioning just right, especially across viewports and for responsive development.

What’s special about SVG is that all the positioning is based on a coordinate system, a little like the game Battleship. That means deciding where everything goes and how it’s drawn, as well as how it’s relative to each other, can be really straightforward to reason about. CSS positioning is for layout, which is great because you have things that correspond to one another in terms of the flow of the document. This otherwise positive trait is harder to work with if you’re making a component that’s very particular, with overlapping and precisely placed elements.

Truly, once you learn SVG, you can draw anything, and have it scale on any device. Even this very site uses SVG for custom UI elements, such as my avatar, above (meta!).

That little half circle below the author image is just SVG markup.

We won’t cover everything about SVGs in this post (you can learn some of those fundamentals here, here, here and here), but in order to illustrate the possibilities that SVG opens up for UI component development, let’s talk through one particular use case and break down how we would think about building something custom.

The timeline task list component

Recently, I was working on a project with my team at Netlify. We wanted to show the viewer which video in a series of videos in a course they were currently watching. In other words, we wanted to make some sort of thing that’s like a todo list, but shows overall progress as items are completed. (We made a free space-themed learning platform and it’s hella cool. Yes, I said hella.)

Here’s how that looks:

So how would we go about this? I’ll show an example in both Vue and React so that you can see how it might work in both frameworks.

The Vue version

We decided to make the platform in Next.js for dogfooding purposes (i.e. trying out our own Next on Netlify build plugin), but I’m more fluent in Vue so I wrote the initial prototype in Vue and ported it over to React.

Here is the full CodePen demo:

CodePen Embed Fallback

Let’s walk through this code a bit. First off, this is a single file component (SFC), so the template HTML, reactive script, and scoped styles are all encapsulated in this one file.

We’ll store some dummy tasks in data, including whether each task is completed or not. We’ll also make a method we can call on a click directive so that we can toggle whether the state is done or not.

<script> export default { data() { return { tasks: [ { name: 'thing', done: false }, // ... ] }; }, methods: { selectThis(index) { this.tasks[index].done = !this.tasks[index].done } } }; </script>

Now, what we want to do is create an SVG that has a flexible viewBox depending on the amount of elements. We also want to tell screen readers that this a presentational element and that we will provide a title with a unique id of timeline. (Get more information on creating accessible SVGs.)

<template> <div id="app"> <div> <svg :viewBox="`0 0 30 ${tasks.length * 50}`" xmlns="http://www.w3.org/2000/svg" width="30" stroke="currentColor" fill="white" aria-labelledby="timeline" role="presentation"> <title id="timeline">timeline element</title> <!-- ... --> </svg> </div> </div> </template>

The stroke is set to currentColor to allow for some flexibility — if we want to reuse the component in multiple places, it will inherit whatever color is used on the encapsulating div.

Next, inside the SVG, we want to create a vertical line that’s the length of the task list. Lines are fairly straightforward. We have x1 and x2 values (where the line is plotted on the x-axis), and similarly, y1 and y2.

<line x1="10" x2="10" :y1="num2" :y2="tasks.length * num1 - num2" />

The x-axis stays consistently at 10 because we’re drawing a line downward rather than left-to-right. We’ll store two numbers in data: the amount we want our spacing to be, which will be num1, and the amount we want our margin to be, which will be num2.

data() { return { num1: 32, num2: 15, // ... } }

The y-axis starts with num2, which is subtracted from the end, as well as the margin. The tasks.length is multiplied by the spacing, which is num1.

Now, we’ll need the circles that lie on the line. Each circle is an indicator for whether a task has been completed or not. We’ll need one circle for each task, so we’ll use v-for with a unique key, which is the index (and is safe to use here as they will never reorder). We’ll connect the click directive with our method and pass in the index as a param as well.

CIrcles in SVG are made up of three attributes. The middle of the circle is plotted at cx and cy, and then we draw a radius with r. Like the line, cx starts at 10. The radius is 4 because that’s what’s readable at this scale. cy will be spaced like the line: index times the spacing (num1), plus the margin (num2).

Finally, we’ll put use a ternary to set the fill. If the task is done, it will be filled with currentColor. If not, it will be filled with white (or whatever the background is). This could be filled with a prop that gets passed in the background, for instance, where you have light and dark circles.

<circle @click="selectThis(i)" v-for="(task, i) in tasks" :key="task.name" cx="10" r="4" :cy="i * num1 + num2" :fill="task.done ? 'currentColor' : 'white'" class="select"/>

Finally, we are using CSS grid to align a div with the names of tasks. This is laid out much in the same way, where we’re looping through the tasks, and are also tied to that same click event to toggle the done state.

<template> <div> <div @click="selectThis(i)" v-for="(task, i) in tasks" :key="task.name" class="select"> {{ task.name }} </div> </div> </template> The React version

Here is where we ended up with the React version. We’re working towards open sourcing this so that you can see the full code and its history. Here are a few modifications:

  • We’re using CSS modules rather than the SCFs in Vue
  • We’re importing the Next.js link, so that rather than toggling a “done” state, we’re taking a user to a dynamic page in Next.js
  • The tasks we’re using are actually stages of the course —or “Mission” as we call them — which are passed in here rather than held by the component.

Most of the other functionality is the same :)

import styles from './MissionTracker.module.css'; import React, { useState } from 'react'; import Link from 'next/link'; function MissionTracker({ currentMission, currentStage, stages }) { const [tasks, setTasks] = useState([...stages]); const num1 = [32]; const num2 = [15]; const updateDoneTasks = (index) => () => { let tasksCopy = [...tasks]; tasksCopy[index].done = !tasksCopy[index].done; setTasks(tasksCopy); }; const taskTextStyles = (task) => { const baseStyles = `${styles['tracker-select']} ${styles['task-label']}`; if (currentStage === task.slug.current) { return baseStyles + ` ${styles['is-current-task']}`; } else { return baseStyles; } }; return ( <div className={styles.container}> <section> {tasks.map((task, index) => ( <div key={`mt-${task.slug}-${index}`} className={taskTextStyles(task)} > <Link href={`/learn/${currentMission}/${task.slug.current}`}> {task.title} </Link> </div> ))} </section> <section> <svg viewBox={`0 0 30 ${tasks.length * 50}`} className={styles['tracker-svg']} xmlns="http://www.w3.org/2000/svg" width="30" stroke="currentColor" fill="white" aria-labelledby="timeline" role="presentation" > <title id="timeline">timeline element</title> <line x1="10" x2="10" y1={num2} y2={tasks.length * num1 - num2} /> {tasks.map((task, index) => ( <circle key={`mt-circle-${task.name}-${index}`} onClick={updateDoneTasks(index)} cx="10" r="4" cy={index * +num1 + +num2} fill={ task.slug.current === currentStage ? 'currentColor' : 'black' } className={styles['tracker-select']} /> ))} </svg> </section> </div> ); } export default MissionTracker; Final version

You can see the final working version here:

See Site

This component is flexible enough to accommodate lists small and large, multiple browsers, and responsive sizing. It also allows the user to have better understanding of where they are in their progress in the course.

But this is just one component. You can make any number of UI elements: knobs, controls, progress indicators, loaders… the sky’s the limit. You can style them with CSS, or inline styles, you can have them update based on props, on context, on reactive data, the sky’s the limit! I hope this opens some doors on how you yourself can develop more engaging UI elements for the web.

The post Creating UI Components in SVG appeared first on CSS-Tricks.

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

Graphery SVG

Css Tricks - Mon, 11/23/2020 - 1:33pm

I’ve compared SVG and Canvas before. If you’re trying to decide between them, read that. I’d say the #1 difference between them is vector (SVG) versus raster (Canvas). But the #2 difference is how you work with them. SVG is declarative, as in, literal elements that express what they are through attributes and content. Canvas is imperative, as in, you script instructions for it to follow.

Canvas is a JavaScript API, so it may jive well with JavaScript developers or environments where the UI being built is otherwise JavaScript-based. But SVG is in the (and has a) DOM, and the DOM has APIs too! That means you can script SVG if you like. It’s just, arguably, not particularly convenient. I just saw Graphery SVG which is clearly an attempt to rectify that.

How you’d script the creation of a pink rectangle with standard DOM APIs:

const div = document.querySelector('#drawing'); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '100%'); svg.setAttribute('height', '100%'); div.appendChild(svg); const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); rect.setAttribute('x', '10'); rect.setAttribute('y', '10'); rect.setAttribute('width', '90'); rect.setAttribute('height', '90'); rect.setAttribute('fill', '#F06'); svg.appendChild(rect);

With Graphery SVG:

const svg = gySVG().width('100%').height('100%'); const rect = svg.add('rect').x(10).y(10).width(90).height(90).fill('#f06'); svg.attachTo('#drawing');

Gotta love that chaining. High five, jQuery.

Direct Link to ArticlePermalink

The post Graphery SVG appeared first on CSS-Tricks.

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

What’s Missing from CSS?

Css Tricks - Sat, 11/21/2020 - 9:44am

The survey results from the State of CSS aren’t out yet, but they made this landing page that randomly shows you what one person wrote to answer that question. Just clicking the reload button a bunch, I get the sense that the top answers are:

  • Container Queries
  • Parent Selectors
  • Nesting
  • Something extremely odd that doesn’t really make sense and makes me wonder about people

Direct Link to ArticlePermalink

The post What’s Missing from CSS? appeared first on CSS-Tricks.

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

How You Might Build a Modern Day Webring

Css Tricks - Fri, 11/20/2020 - 11:18am

I’m sure different people picture different things when they think about webrings, so let me clarify what I picture. I see an element on a website that:

  1. Signifies this site is part of a webring
  2. Allows you to move to the next or previous site of the webring
  3. Maybe has other functionality like going to a “random” site or seeing the complete list

But then another major thing:

  1. Site owners don’t have to do much. They just plop (it?) on the site and a functional webring UI is there.

So like this:

A Calvin & Hobbes webring UI that comes up all the time when you search the web about webrings

How did it used to work? You know what? I have no idea. My guess is that it was an ancient <frameset><frame /></frameset> situation, but this stuff is before my time a bit. How might we do this today?

Well, we could use an <iframe>, I guess. That’s what sites like YouTube do when they give “embed code” as an HTML snippet. Sites like Twitter and CodePen give you a <div> (or whatever semantic HTML) and a <script>, so that there can be fallback content and the script enhances it into an <iframe>. An <iframe> might be fine, as it asks very little of the site owner, but they are known to be fairly bad for performance. It’s a whole document inside another document, after all. Plus, they don’t offer much by the way of customization. You get what you get.

Another problem with an iframe is that… how would it know what site it is currently embedded on? Maybe a URL parameter? Maybe a postMessage from the parent page? Not super clean if you ask me.

I think a Web Component might be the way to go here, as long as we’re thinking modern. We could make a custom element like <webring-*>. Let’s do that, and make it for CSS sites specifically. That’ll give us the opportunity to send in the current site with an attribute, like this:

<webring-css site="http://css-tricks.com"> This is gonna boot itself up into webring in a minute. </webring-css>

That solves the technology choice. Now we need to figure out the global place to store the data. Because, well, a webring needs to be able to be updated. Sites need to be able to be added and removed from the webring without the other sites on the webring needing to do anything.

For fairly simple data like this, a JSON file on GitHub seems to be a perfectly modern choice. Let’s do that.

Now everyone can see all the sites in the webring in a fairly readable fashion. Plus, they could submit Pull Requests against it to add/remove sites (feel free).

Getting that data from our web component is a fetch away:

fetch(`https://raw.githubusercontent.com/CSS-Tricks/css-webring/main/webring.json`) .then((response) => response.json()) .then((sites) => { // Got the data. });

We’ll fire that off when our web component mounts. Let’s scaffold that…

const DATA_FOR_WEBRING = `https://raw.githubusercontent.com/CSS-Tricks/css-webring/main/webring.json`; const template = document.createElement("template"); template.innerHTML = ` <style> /* styles */ </style> <div class="webring"> <!-- content --> </div>`; class WebRing extends HTMLElement { connectedCallback() { this.attachShadow({ mode: "open" }); this.shadowRoot.appendChild(template.content.cloneNode(true)); fetch(DATA_FOR_WEBRING) .then((response) => response.json()) .then((sites) => { // update template with data }); } } window.customElements.define("webring-css", WebRing);

The rest of this isn’t so terribly interesting that I feel like we need to go through it step by step. I’ll just blog-sketch it for you:

  1. Pull the attribute off the web component so we can see what the current site is
  2. Match the current site in the data
  3. Build out Next, Previous, and Random links from the data in a template
  4. Update the HTML in the template

And voilà!

CodePen Embed Fallback Could you do a lot more with this, like error handling, better design, and better everything?

Yes.

The post How You Might Build a Modern Day Webring appeared first on CSS-Tricks.

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

console.log({ myVariable });

Css Tricks - Thu, 11/19/2020 - 12:30pm

I think this might be my most popular tweet of all time, but I’m not sure how to verify that these days. I’ll restate this neat little trick here because blogging is cool and fun.

I used to do this a lot while debugging JavaScript:

console.log("myVariable: ", myVariable);

But now I do this because it’s just easier to type quickly:

console.log({ myVariable });

And you don’t miss out on anything in DevTools:

Now that this is a blog post, I can elaborate a smidge…

The output display there (and really, console.log itself) is a DevTools thing, but the syntax isn’t. By using curly brackets in JavaScript, I’m creating an object. I don’t have to assign an object to anything, it can just be.

{ foo: "bar" }

You see that a lot when passing an object to a function, like myFunction({ config: options });. The “trick” is that when you’re making an object, you can “shorthand” it by providing only a variable, which makes it implied). So like:

const fruit = "apple"; // Shorthand let x = { fruit } console.log(x); // Normal way, literally the same exact thing x = { "fruit": fruit } console.log(x); // identical

When you have a variable and you log it like console.log({ myVariable }); you’re using that shorthand object creation syntax and it gets logged like the object it becomes.

One strike against this idea is that sometimes DevTools chooses to output it as ▶ Object and you have to click to open to see the value. I don’t know what the heuristics are of when it chooses to do that and when it doesn’t. If you don’t like that, or the output format of an object in general, you might prefer the console.table({ myVariable }); format:

The post console.log({ myVariable }); appeared first on CSS-Tricks.

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

mediastack

Css Tricks - Thu, 11/19/2020 - 12:05pm

Have you ever had the idea for a website or new app that involved showing news content? You don’t have to create content yourself to have the right to build an innovative news reading experience. I remember when Flipboard came out. They didn’t (and still don’t) actually produce content — they just made a fantastic experience for reading it, and did very well with that.

Where do you get that news content? mediastack. You’re going to need a great API for delivering news content, and that’s exactly what mediastack is.

You sign up and get an API key. It’s free to use for 500 requests/month, which is enough to prototype what you’re building, particularly if you’re being smart about caching responses. Then, when you need more requests, it scales up very reasonably in price.

Gotta love a good API that does exactly what you need it to do, so you can get to work.

Another thing I like about mediastack is that it’s from apilayer, who have been doing APIs like this for a long time. Their whole business is about providing purpose-built APIs for tasks that developers need to do. I like it when companies are incentivized to do a good job for you because you’re very directly their customer and using their product for exactly what it’s for.

The post mediastack appeared first on CSS-Tricks.

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

grid-auto-flow : CSS Grid :: flex-direction : Flexbox

Css Tricks - Wed, 11/18/2020 - 3:11pm

When setting a parent element to display: flex, its child elements align left-to-right like this:

CodePen Embed Fallback

Now, one of the neat things we can do with flexbox is change the direction so that child elements are stacked vertically on top of each other in a column. We can do that with the flex-direction property (or with the flex-flow shorthand):

CodePen Embed Fallback

Okay, cool. But how would I do something like this with CSS Grid? As in, let’s say I want all those child elements to be aligned like this:

1 3 5 7 -------- 2 4 6 8

…instead of this:

1 2 3 4 -------- 5 6 7 8

By default, when I set a parent element to use CSS Grid, the elements will be positioned left-to-right just like flexbox. In the example below I’m telling the grid to have 6 columns and 2 rows, then let the child elements fill up each column of the first row before they fill up the columns of the second. You know, standard line wrapping behavior.

.parent { display: grid; grid-template-columns: repeat(6, 1fr); grid-template-rows: repeat(2, 150px); gap: 20px; } CodePen Embed Fallback

Basically what I want here is the opposite: I want our child elements to fill up column 1, row 1 and row 2, then move on to the next column. In other words, column wrapping! I know that if I create a grid with rows and columns I could individually place those elements into those positions. Like so:

.parent { display: grid; grid-template-columns: repeat(6, 1fr); grid-template-rows: repeat(6, 150px); } .child-1 { grid-column: 1; grid-row: 1; } .child-2 { grid-column: 1; grid-row: 2; } .child-3 { grid-column: 2; grid-row: 1; } /* etc, etc. */ CodePen Embed Fallback

Okay, neat! This gets me what I want but it’s a giant pain having to individually set the position of each item. It feels like I’m using position: absolute and it doesn’t feel particularly smart. So what if I just wanted this layout to be done for me, so that each new child element would align into the correct spot…correctly?

What I’m asking for (I think) is this: is there a CSS Grid version of flex-direction: column?

Well, after searching around a bit, Rachel Andrew pointed me to the correct answer in her rather excellent playground, Grid by Example. And as you can see in this demo, Rachel shows us how to do just that:

CodePen Embed Fallback

Neato! Rachel does this with the grid-auto-flow property: it tells a grid container how to fill the unoccupied space with child elements. So I can do that just by writing this:

.parent { display: grid; grid-auto-flow: column; /* set up columns and rows here */ }

By default, child elements of a grid will fill up each column until a row is filled, then it’ll flow into the next beneath it. This is why the default for grid-auto-flow is set to row because we’re filling up rows of the grid first. But if we set it to column, then each new element will fill up all the space of column 1 before moving on to column 2, etc.

.parent { display: grid; grid-auto-flow: column; grid-template-columns: repeat(6, 1fr); grid-template-rows: repeat(2, 150px); } CodePen Embed Fallback

This is what the flow part of grid-auto-flow means and for the longest time I ignored the property because it seemed (don’t laugh) scary. Just reading the word grid-auto-flow is enough to make me want to shut off my laptop and walk into the ocean.

But! It’s a pretty useful property and makes a ton of sense, especially if you think of it as the CSS Grid version of flex-direction.

The post grid-auto-flow : CSS Grid :: flex-direction : Flexbox appeared first on CSS-Tricks.

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

Syndicate content
©2003 - Present Akamai Design & Development.