Hide the body (with JavaScript) right away with with a CSS class that declares opacity: 0
Wait for all the JavaScript to execute
Unhide the body by transitioning it back to opacity: 1
Like this:
Louis demonstrates a callback method, as well as mentioning you could wait for window.load or a DOM Ready event. I suppose you could also just have the line that sets the className to visible as the very last line of script that runs like I did above.
Louis knows it’s not particularly en vogue:
I know nowadays we’re obsessed in this industry with gaining every millisecond in page performance. But in a couple of projects that I recently overhauled, I added a subtle and clean loading mechanism that I think makes the experience nicer, even if it does ultimately slightly delay the time that the user is able to start interacting with my page.
I think of stuff like font-display: swap; which is dedicated to rendering your text as absolutely fast as possible, FOUT be damned, rather than chiller options.
Here’s a fun page coming from secretGeek.net. You don’t normally think “fun” with brutalist minimalism but the CSS trickery that makes it work on this page is certainly that.
The HTML is literally displayed on the page as tags. So, in a sense, the HTML is both the page markup and the content. The design is so minimal (or “naked”) that it’s code leaks through! Very cool.
The page explains the trick, but I’ll paraphrase it here:
Everything is a block-level element via { display:block; }
…except for anchors, code, emphasis and strong, which remain inline with a,code,em,strong {display:inline}
Use ::before and ::after to display the HTML tags as content (e.g. p::before { content: '<p>'})
A straightforward post with some perf data from Tomas Pustelnik. It’s a good reminder that CSS is a crucial part of thinking web performance, and for a huge reason:
Any time [the browser] encounters any external resource (CSS, JS, images, etc.) it will assign it a download priority and initiate its download. Priorities are important because some resources are critical to render a page (eg. main stylesheet and JS files) while others may be less important (like images or stylesheets for other media types).
In the case of CSS, this priority is usually high because stylesheets are necessary to create CSSOM (CSS Object Model). To render a webpage browser has to construct both DOM and CSSOM.
That’s why CSS is often referred to as a “blocking” resource. That’s desirable to some degree: we wouldn’t want to see flash-of-unstyled-websites. But we get real perf gains when we make CSS smaller because it’s quicker to download, parse, and apply.
Aside from the techniques in the post, I’m sure advocates of atomic/all-utility CSS would love it pointed out that the stylesheets from those approaches are generally way smaller, and thus more performant. CSS-in-JS approaches will sometimes bundle styles into scripts so, to be fair, you get a little perf gain at the top by not loading the CSS there, but a perf loss from increasing the JavaScript bundle size in the process. (I haven’t seen a study with a fair comparison though, so I don’t know if it’s a wash or what.)
There are many different approaches to menus on websites. Some menus are persistent, always in view and display all the options. Other menus are hidden by design and need to be opened to view the options. And there are even additional approaches on how hidden menus reveal their menu items. Some fly out and overlap the content, some push the content away, and others will do some sort of full-screen deal.
Whatever the approach, they all have their pros and cons and the right one depends on the situation where it’s being used. Frankly, I tend to like fly-out menus in general. Not for all cases, of course. But when I’m looking for a menu that is stingy on real estate and easy to access, they’re hard to beat.
What I don’t like about them is how often they conflict with the content of the page. A fly-out menu, at best, obscures the content and, at worst, removes it completely from the UI.
I tried taking another approach. It has the persistence and availability of a fixed position as well as the space-saving attributes of a hidden menu that flies out, only without removing the user from the current content of the page.
Here’s how I made it.
The toggle
We’re building a menu that has two states — open and closed — and it toggles between the two. This is where the Checkbox Hack comes into play. It’s perfect because a checkbox has two common interactive states — checked and unchecked (there’s also the indeterminate) — that can be used to trigger those states.
The checkbox is hidden and placed under the menu icon with CSS, so the user never sees it even though they interact with it. Checking the box (or, ahem, the menu icon) reveals the menu. Unchecking it hides it. Simple as that. We don’t even need JavaScript to do the lifting!
Of course, the Checkbox Hack isn’t the only way to do this, and if you want to toggle a class to open and close the menu with JavaScript, that’s absolutely fine.
It’s important the checkbox precedes the main content in the source code, because the :checked selector we’re going to ultimately write to make this work needs to use a sibling selector. If that’ll cause layout concerns for you, use Grid or Flexbox for your layouts as they are source order independent, like how I used its advantage for counting in CSS.
The checkbox’s default style (added by the browser) is stripped out, using the appearance CSS property, before adding its pseudo element with the menu icon so that the user doesn’t see the square of the checkbox.
…and the baseline CSS for the Checkbox Hack and menu icon:
/* Hide checkbox and reset styles */ input[type="checkbox"] { appearance: initial; /* removes the square box */ border: 0; margin: 0; outline: none; /* removes default margin, border and outline */ width: 30px; height: 30px; /* sets the menu icon dimensions */ z-index: 1; /* makes sure it stacks on top */ } /* Menu icon */ input::after { content: "55"; display: block; font: 25pt/30px "georgia"; text-indent: 10px; width: 100%; height: 100%; } /* Page content container */ #page { background: url("earbuds.jpg") #ebebeb center/cover; width: 100%; height: 100%; }
I threw in the styles for the #page content as well, which is going to be a full size background image.
The transition
Two things happen when the menu control is clicked. First, the menu icon changes to an × mark, symbolizing that it can be clicked to close the menu. So, we select the ::after pseudo element of checkbox input when the input is in a :checked state:
input:checked::after { content: "d7"; /* changes to × mark */ color: #ebebeb; }
Second, the main content (our “earbuds” image) transforms, revealing the menu underneath. It moves to the right, rotates and scales down, and its left side corners get angular. This is to give the appearance of the content getting pushed back, like a door that swings open.
I used clip-path to change the corners of the image.
Since we’re applying a transition on the transformations, we need an initial clip-path value on the #page so there’s something to transition from. We’ll also drop a transition on #page while we’re at it because that will allow it to close as smoothly as it opens.
We’re basically done with the core design and code. When the checkbox is unchecked (by clicking the × mark) the transformation on the earbud image will automatically be undone and it’ll be brought back to the front and centre.
A sprinkle of JavaScript
Even though we have what we’re looking for, there’s still one more thing that would give this a nice boost in the UX department: close the menu when clicking (or tapping) the #page element. That way, the user doesn’t need to look for or even use the × mark to get back to the content.
Since this is merely an additional way to hide the menu, we can use JavaScript. And if JavaScript is disabled for some reason? No big deal. It’s just an enhancement that doesn’t prevent the menu from working without it.
What this three-liner does is add a click event handler over the #page element that un-checks the checkbox if the checkbox is in a :checked state, which closes the menu.
We’ve been looking at a demo made for a vertical/portrait design, but works just as well at larger landscape screen sizes, depending on the content we’re working with.
This is just one approach or take on the typical fly-out menu. Animation opens up lots of possibilities and there are probably dozens of other ideas you might have in mind. In fact, I’d love to hear (or better yet, see) them, so please share!
The trick is to throw the scroll position into localStorage right before the page is exited, and when loaded, grab that value and scroll to it. I’ll retype it from the tweet…
let sidebar = document.querySelector(".sidebar"); let top = localStorage.getItem("sidebar-scroll"); if (top !== null) { sidebar.scrollTop = parseInt(top, 10); } window.addEventListener("beforeunload", () => { localStorage.setItem("sidebar-scroll", sidebar.scrollTop); });
What is surprising is that you don’t get a flash-of-wrong-scroll-position. I wonder why? Maybe it has to do with fancy paint holding stuff browsers are doing now? Not sure.
Flywheel is my WordPress hosting partner here. I use Local every day for my WordPress local development environment and use their hosting for all my WordPress sites as part of my whole flow, so I’m glad they aren’t just a sponsor but a product I use and like.
Last November some of their crew came out and shot some photos and video with me, which was cool and fun and made me feel famous lolll.
Check out some photos that Kimberly Bailey took while the gang was here.
And the video!
We sat down with @chriscoyier (CSS-Tricks, CodePen, and Shop Talk Pro) to chat about his decision to move his sites to Flywheel. 👏👏 pic.twitter.com/zh5zPojTOm
Anyway feel free to check out that landing page and if your company is headed to make a WordPress hosting decision soon, might as well use my referral link 😉
We can use JavaScript to get the value of a CSS custom property. Robin wrote up a detailed explanation about this in Get a CSS Custom Property Value with JavaScript. To review, let’s say we’ve declared a single custom property on the HTML element:
html { --color-accent: #00eb9b; }
In JavaScript, we can access the value with getComputedStyle and getPropertyValue:
Perfect. Now we have access to our accent color in JavaScript. You know what’s cool? If we change that color in CSS, it updates in JavaScript as well! Handy.
What happens, though, when it’s not just one property we need access to in JavaScript, but a whole bunch of them?
We’re repeating ourselves a lot. We could shorten each one of these lines by abstracting the common tasks to a function.
const getCSSProp = (element, propName) => getComputedStyle(element).getPropertyValue(propName); const colorAccent = getCSSProp(document.documentElement, '--color-accent'); // #00eb9b // repeat for each custom property...
That helps reduce code repetition, but we still have a less-than-ideal situation. Every time we add a custom property in CSS, we have to write another line of JavaScript to access it. This can and does work fine if we only have a few custom properties. I’ve used this setup on production projects before. But, it’s also possible to automate this.
Let’s walk through the process of automating it by making a working thing.
What are we making?
We’ll make a color palette, which is a common feature in pattern libraries. We’ll generate a grid of color swatches from our CSS custom properties.
Here’s the complete demo that we’ll build step-by-step.
Here’s what we’re aiming for.
Let’s set the stage. We’ll use an unordered list to display our palette. Each swatch is a <li> element that we’ll render with JavaScript.
<ul class="colors"></ul>
The CSS for the grid layout isn’t pertinent to the technique in this post, so we won’t look at in detail. It’s available in the CodePen demo.
Now that we have our HTML and CSS in place, we’ll focus on the JavaScript. Here’s an outline of what we’ll do with our code:
Get all stylesheets on a page, both external and internal
Discard any stylesheets hosted on third-party domains
Get all rules for the remaining stylesheets
Discard any rules that aren’t basic style rules
Get the name and value of all CSS properties
Discard non-custom CSS properties
Build HTML to display the color swatches
Let’s get to it.
Step 1: Get all stylesheets on a page
The first thing we need to do is get all external and internal stylesheets on the current page. Stylesheets are available as members of the global document.
document.styleSheets
That returns an array-like object. We want to use array methods, so we’ll convert it to an array. Let’s also put this in a function that we’ll use throughout this post.
When we invoke getCSSCustomPropIndex, we see an array of CSSStyleSheet objects, one for each external and internal stylesheet on the current page.
Step 2: Discard third-party stylesheets
If our script is running on https://example.com any stylesheet we want to inspect must also be on https://example.com. This is a security feature. From the MDN docs for CSSStyleSheet:
In some browsers, if a stylesheet is loaded from a different domain, accessing cssRules results in SecurityError.
That means that if the current page links to a stylesheet hosted on https://some-cdn.com, we can’t get custom properties — or any styles — from it. The approach we’re taking here only works for stylesheets hosted on the current domain.
CSSStyleSheet objects have an href property. Its value is the full URL to the stylesheet, like https://example.com/styles.css. Internal stylesheets have an href property, but the value will be null.
Let’s write a function that discards third-party stylesheets. We’ll do that by comparing the stylesheet’s href value to the current location.origin.
With the third-party stylesheets discarded, we can inspect the contents of those remaining.
Step 3: Get all rules for the remaining stylesheets
Our goal for getCSSCustomPropIndex is to produce an array of arrays. To get there, we’ll use a combination of array methods to loop through, find values we want, and combine them. Let’s take a first step in that direction by producing an array containing every style rule.
We use reduce and concat because we want to produce a flat array where every first-level element is what we’re interested in. In this snippet, we iterate over individual CSSStyleSheet objects. For each one of them, we need its cssRules. From the MDN docs:
The read-only CSSStyleSheet property cssRules returns a live CSSRuleList which provides a real-time, up-to-date list of every CSS rule which comprises the stylesheet. Each item in the list is a CSSRule defining a single rule.
Each CSS rule is the selector, braces, and property declarations. We use the spread operator ...sheet.cssRules to take every rule out of the cssRules object and place it in finalArr. When we log the output of getCSSCustomPropIndex, we get a single-level array of CSSRule objects.
This gives us all the CSS rules for all the stylesheets. We want to discard some of those, so let’s move on.
Step 4: Discard any rules that aren’t basic style rules
CSS rules come in different types. CSS specs define each of the types with a constant name and integer. The most common type of rule is the CSSStyleRule. Another type of rule is the CSSMediaRule. We use those to define media queries, like @media (min-width: 400px) {}. Other types include CSSSupportsRule, CSSFontFaceRule, and CSSKeyframesRule. See the Type constants section of the MDN docs for CSSRule for the full list.
We’re only interested in rules where we define custom properties and, for the purposes in this post, we’ll focus on CSSStyleRule. That does leave out the CSSMediaRule rule type where it’s valid to define custom properties. We could use an approach that’s similar to what we’re using to extract custom properties in this demo, but we’ll exclude this specific rule type to limit the scope of the demo.
To narrow our focus to style rules, we’ll write another array filter:
const isStyleRule = (rule) => rule.type === 1;
Every CSSRule has a type property that returns the integer for that type constant. We use isStyleRule to filter sheet.cssRules.
One thing to note is that we are wrapping ...sheet.cssRules in brackets so we can use the array method filter.
Our stylesheet only had CSSStyleRules so the demo results are the same as before. If our stylesheet had media queries or font-face declarations, isStyleRule would discard them.
Step 5: Get the name and value of all properties
Now that we have the rules we want, we can get the properties that make them up. CSSStyleRule objects have a style property that is a CSSStyleDeclaration object. It’s made up of standard CSS properties, like color, font-family, and border-radius, plus custom properties. Let’s add that to our getCSSCustomPropIndex function so that it looks at every rule, building an array of arrays along the way:
If we invoke this now, we get an empty array. We have more work to do, but this lays the foundation. Because we want to end up with an array, we start with an empty array by using the accumulator, which is the second parameter of reduce. In the body of the reduce callback function, we have a placeholder variable, props, where we’ll gather the properties. The return statement combines the array from the previous iteration — the accumulator — with the current props array.
Right now, both are empty arrays. We need to use rule.style to populate props with an array for every property/value in the current rule:
rule.style is array-like, so we use the spread operator again to put each member of it into an array that we loop over with map. In the map callback, we return an array with two members. The first member is propName (which includes color, font-family, --color-accent, etc.). The second member is the value of each property. To get that, we use the getPropertyValue method of CSSStyleDeclaration. It takes a single parameter, the string name of the CSS property.
We use trim on both the name and value to make sure we don’t include any leading or trailing whitespace that sometimes gets left behind.
Now when we invoke getCSSCustomPropIndex, we get an array of arrays. Every child array contains a CSS property name and a value.
This is what we’re looking for! Well, almost. We’re getting every property in addition to custom properties. We need one more filter to remove those standard properties because all we want are the custom properties.
Step 6: Discard non-custom properties
To determine if a property is custom, we can look at the name. We know custom properties must start with two dashes (--). That’s unique in the CSS world, so we can use that to write a filter function:
In the function signature, we have ([propName]). There, we’re using array destructuring to access the first member of every child array in props. From there, we do an indexOf check on the name of the property. If -- is not at the beginning of the prop name, then we don’t include it in the props array.
When we log the result, we have the exact output we’re looking for: An array of arrays for every custom property and its value with no other properties.
Looking more toward the future, creating the property/value map doesn’t have to require so much code. There’s an alternative in the CSS Typed Object Model Level 1 draft that uses CSSStyleRule.styleMap. The styleMap property is an array-like object of every property/value of a CSS rule. We don’t have it yet, but If we did, we could shorten our above code by removing the map:
At the time of this writing, Chrome and Edge have implementations of styleMap but no other major browsers do. Because styleMap is in a draft, there’s no guarantee that we’ll actually get it, and there’s no sense using it for this demo. Still, it’s fun to know it’s a future possibility!
We have the data structure we want. Now let’s use the data to display color swatches.
Step 7: Build HTML to display the color swatches
Getting the data into the exact shape we needed was the hard work. We need one more bit of JavaScript to render our beautiful color swatches. Instead of logging the output of getCSSCustomPropIndex, let’s store it in variable.
We use reduce to iterate over the custom prop index and build a single HTML-looking string for innerHTML. But reduce isn’t the only way to do this. We could use a map and join or forEach. Any method of building the string will work here. This is just my preferred way to do it.
I want to highlight a couple specific bits of code. In the reduce callback signature, we’re using array destructuring again with [prop, val], this time to access both members of each child array. We then use the prop and val variables in the body of the function.
To show the example of each color, we use a b element with an inline style:
But how does that set a background color? In the full CSS we use the custom property --color as the value of background-color for each .color__swatch. Because external CSS rules inherit from inline styles, --color is the value we set on the b element.
.color__swatch { background-color: var(--color); /* other properties */ }
We now have an HTML display of color swatches representing our CSS custom properties!
This demo focuses on colors, but the technique isn’t limited to custom color props. There’s no reason we couldn’t expand this approach to generate other sections of a pattern library, like fonts, spacing, grid settings, etc. Anything that might be stored as a custom property can be displayed on a page automatically using this technique.
Only Firefox supports it, but if you return a request with a header like this:
Header add Link "<style.css>;rel=stylesheet;media=all"
…that will link to that stylesheet without you having to do it in the HTML. Louis Lazaris digs into it:
[…] the only thing I can think of that could justify use for this in production is as a way to include some Firefox-only CSS, which Eric Meyer mentions as a possibility in an old post on this subject. But it’s not guaranteed to always only work in Firefox, so that’s still a problem.
Do with this what you like, but it’s extremely unlikely that this will have any use in a real project.
I keep running across these super useful one page sites, and they keep being by the same person! Like this one with over 100 vanilla JavaScript DOM manipulation recipes, this similar one full of one-liners, and this one with loads of layouts. For that last one, making 91 icons for all those design patterns is impressive alone. High five, Phuoc.
This is my favorite sort of marketing. Some of the products aren’t free, like the React PDF Viewer. How do you get people to know about your paid thing? Give a bunch of useful stuff away for free and have the paid thing sitting right next to it.
Scrolling is something we all know and do on the web to the extent that it’s an expectation or perhaps even a habit, like brushing our teeth. That’s probably why we don’t put too much thought into designing the scrolling experience — it’s a well-known basic function. In fact, the popular “there is no fold” saying comes from the idea that people know how to scroll and there is no arbitrary line that people don’t go under.
Scroll-based features tend to involve some bespoke concoction of CSS and JavaScript. That’s because there simply aren’t that many native features available to do it. But what if we could accomplish something that only uses CSS?
Take this ingenious horizontal scrollbar with CSS, for instance. I want to do something similar, but to indicate scrolled sections rather than capture continuous scrolling. In other words, rather than increasing the length of the indicator during scroll, I only want to increase the length when a certain section of the page has been reached.
Like this:
Here’s my plan: Each section carries an indicator that’s undetectable until it reaches the top of the screen. That’s where it becomes visible by changing color and sticks to the top of the viewport.
The exact opposite should happen in reverse: the indicator will follow along when scrolling back up the screen, camouflaging itself back to being undetected to the naked eye.
There are two key parts to this. The first is for the indicator to change color when it’s near the top of the screen. The second is for the indicator to stay put at the top of the screen and come down only when its section is scrolled down to.
The second one is easy to do: we use position: sticky; on our elements. When a page is scrolled, a sticky element sticks to a given position on the screen within its parent container.
That brings us to changing colors. Since the background of an HTML document is white by default, I’m keeping white as the base color for the demo. This means the indicator should look white when it’s over the base color and turn to some other color when it’s over the indicator bar at the top of the screen.
The dashed indicator is currently invisible, but becomes visible when it sticks to the top and blends with the background color of the indicator container.
This is where CSS blend modes come into play. They give us so many options to create a variety of color amalgams. I’m going to go with the overlay value. This one is quite dynamic in nature. I won’t explain the blend in depth (because the CSS-Tricks Alamanac already does a good job of that) but taking this demo into account, I’ll say this: when the background color is white the resulting foreground color is white; and when the background is some other color, the resulting color is darker or lighter, depending on the color it’s mixed with.
The indicator stops in the demo are black. But, because of the blend, we see them as white because they are on a white background. And when they are over the indicator container element, which is a lovely shade of violet, we see a dark violet indicator stop, because we’re mixing the indicator stop’s black with the indicator container’s violet.
Pretty straightforward, right? There’s a sticky container at the very top that holds the indicators when they reach the top. From there, we have three sections of content, each one topped with an indicator that will stick to the top with the indicator and blend with it.
Here’s the CSS:
.passageStops { background-color: black; /* Each indicator stop is black */ mix-blend-mode: overlay; /* This makes it appear white on a white background */ width: 33.3%; /* Three sections total, so each section is one-third */ top: calc(1em + 3px); } #passage, .passageStops{ height: 10px; } #passageWrapper, .passageStops { position: sticky; /* The container and stops should stick to the top */ z-index: 1; /* Make sure the indicator and stops stay at the forefront */ } #passage { background: violet; /* Will blend with black to make a darker violet indicator */ margin: 0 0 20px 0; } #passageWrapper{ background-color: white; /* Make sure we're working with white to hide indicator stops */ height: 40px; top: 0px; } /* Each stop will shift one-third the width of the indicator container to cover the whole thing when the last section is reached. */ .passageStops:nth-child(4){ margin-left: 33.3%; } .passageStops:nth-child(6){ margin-left: 66.6%; } /* More styling, blah blah. */
The indicators (.passageStops) are black. But the overlay blend mode makes them appear white when it blends with the white background under it. Since there are three sections, each indicator is of one-third width.
The indicators have position: sticky; with a top distance value. This means the indicators will stick once they reach the calculated position from the top of the screen. When that happens, the black indicators that appeared white blend with the violet indicator container, which makes them appear to be a dark violet, representing the new scroll position on the page.
The reverse is also true. When an indicator loses its sticky position, it will move from the violet background of the indicator bar to the white background of the page, hiding it once again… like it was never there!
Here’s the demo again:
That’s it. You can perhaps further experiment with this by having a non-white background with another blend mode, or a gradient for the indicator bar or stops.