Thanks to this tweet by Γlvaro Trigo I found out that you can change the color of the caret β that little blinking | in text inputs β using the caret-color CSS property:
πββοΈ Do note that the caret-color defaults to currentColor, so you don’t need to manually match the color set on an element.
~
Browser support is really great:
π‘ Shown above is a live CanIUse.com table, showing an always up-to-date support table. By the time you are reading this browser support might have become better.
~
Wondering if there were more caret-* properties this turned out to be not the case. A pity though, as I can think of cases where I would like to set the caret-height and caret-thickness, such as emulating the cursor of a terminal:
~
As a fun little experiment Tim Carambat created a Pen that changes the caret color from green to yellow to red as you near the maxlength of the <textarea> you’re typing in.
Back in May 2020 I was very delighted to read that the first Working Draft of the CSS Box Sizing Module Level 4 got published, as it featured an addition to CSS that I’ve been wanting for a long time now: native support for aspect ratio boxes through the new aspect-ratio CSS property.
With Chromium 89 (current Canary) and Firefox 85 (current Nightly) already supporting aspect-ratio unflagged, it’s time to start playing with this new addition and start thinking about dropping all those nasty hacks to mimic aspect ratios in CSS. Let’s take a look β¦
π€ Working Draft (WD)?
The Working Draft (WD) phase is the first phase of the W3C Recommendation Track, and is considered the exploring phase of a W3C spec.
From thereon a spec can become a Candidate Recommendation (CR) to finally land on being a Recommendation (REC). In between those three stages there are two transition stages: Last Call Working Draft (LCWD) and Proposed Recommendation (PR)
In visual form, the Recommendation Track looks something like this:
β οΈ Note that the current implementations in both Firefox Nightly and Chrome Canary are not finalised yet. You can track these bugs to stay up-to-date:
In the example above the .box will have a preferred aspect ratio of 16:9. Since its width is set to 20vw, the resulting height will be 20vw / 16 * 9 = 11.25vw. Easy, right?
The value as set in the example above for aspect-ratio is of the <ratio> type:
It typically consists of two numbers separated by a /. The first parameter targets the width and the second one the height.
It’s also allowed to pass in just a single number. In that case the second number will be we considered to be 1. E.g. a <ratio> of 2 will translate to 2 / 1.
Passing in a 0 for either of the numbers is not allowed.
The spaces around the / are not required, so 2/1 is also a valid <ratio> value.
Another allowed value for the aspect-ratio property β which also is the default β is auto. This indicates that the box has no preferred aspect ratio and should size itself as it normally would.
πββοΈ Hold up! How come images already behave correctly, without needing to define an aspect-ratio?
Images may be commonly used, but they are a quite uncommon type of HTML element:
A replaced element is an element whose content is outside the scope of the CSS formatting model, such as an image or embedded document. For example, the content of the HTML <img> element is often replaced by the image that its src attribute designates.
Just check your DevTools: the browser will make an extra HTTP request for any image and fetch its contents separately. Once loaded, the browser will replace the original img tag with the actual image contents.
The intrinsic dimensions represent a preferred or natural size of the object itself; that is, they are not a function of the context in which the object is used.
Each photo that you take with your phone results in an image that has a certain width and height, which is referred to as the intrinsic or natural width/height. The intrinsic aspect ratio is the ratio between the intrinsic width and intrinsic height.
When the browser has fetched the image and needs to draw it on screen it will take its intrinsic aspect ratio into account to know how big it should be drawn.
When you define width and height attributes on an img, the browser will take those into account when drawing the image on screen. Nowadays browsers even internally map those properties to CSS sizing properties.
βοΈ Do note that you can still set an aspect-ratio on an element that has an intrinsic aspect ratio. In that case your defined aspect-ratio will override the intrinsic aspect ratio.
Setting an aspect-ratio won’t have effect on elements that have both a CSS width and CSS height set to a value other than auto. Only one of width or height can be explicitly set, and the other should remain set to auto.
.box {
width: 20vw;
height: 20vw;
aspect-ratio: 16 / 9; /* won't have any effect! */
}
In case one of the width and height should be set to a percentage based value such as 100%, the targeted box will take a look at the direct parent element’s dimensions to define its value upon that.
.parent {
height: 100px;
}
.parent .box {
height: 100%;
aspect-ratio: 1 / 1; /* .box will be 100px by 100px */
}
There’s some more edge cases here too, but let’s not get too deep into the spec π
Setting an aspect-ratio will tell the browser that this is a preferred aspect ratio. Should the content of the box be larger, then the box will simply grow.
# Using aspect-ratio with a fallback for older browsers
Thanks to the powerful @supports it’s possible to add a fallback for browsers that don’t support aspect-ratio. In the demo below (based upon this demo by Una) a fallback using the padding-top hack is applied:
π I’ve noticed that reading the width/height attribute values using attr() to pass them into aspect-ratio doesn’t seem to work in current Chromium. To cater for that I’m also passing their values by means of a CSS Custom Property β¦
πββοΈ Why doesn’t this iframe demo have a padding-top fallback injected using :after?
Just like images, iframes also are replaced elements. It’s not possible to inject contents using :before/:after on replaced elements.
If you really need to have a fallback, you need to wrap the iframe in a container and apply the aspect-ratio on the container. See Embed Responsively and adjust were needed.
After 8 years of wanting this feature to land in CSS (ref) I’m very happy to see this addition make it into the spec. It’s still a Working Draft right now, but that doesn’t stop me from being excited about it already. I hope you are too π
π This post is part of a series called The future of CSS. More posts in this series:
When building applications on build pipelines like GitHub Actions, Google Cloud Build, CircleCI, etc. every second counts. Here’s this small trick I use to speed up build times: when cloning the repo from its Git source, I instruct Git to do a shallow clone of the single branch it is building.
π‘ If you’re running a prebuilt “git clone” step on your Build Pipeline it most likely already uses this trick. Be sure to double check though.
~
Shallow Cloning
When doing a git clone you, by default, get the entire history of the project along with that. Take this clone of Laravel for example:
That’s a whopping 32004 objects totalling Β±10MiB that have been downloaded, even though the default Laravel branch only counts 66 files spread across 36 directories.
The objects contained in this Β±10MiB make up the entire history of every file and folder the project. To build the project we don’t really need all that, as we’re only interested in the latest version of each file and folder. By leveraging the --depth argument of our git clone command, we can enforce just that. This is what we call Shallow Cloning.
That’s a much speedier clone with only 108 objects, totalling a mere Β±40KiB!
π€ You could argue that 10MiB worth of objects is still OK to clone, but think of scenarios where you have a big βmonorepoβ with plenty of branches β¦ then you’ll be talking about hundreds of wasted MiBs, if not GiBs.
~
Single Branch Cloning
When building a project you’re building only one certain branch. Information about the other branches is irrelevant at that time. To directly clone one specific branch we can use the --branch option to target said branch. With that alone we won’t get there though, we we still need to discard information about other branches. This is where the --single-branch option comes into play:
Here we’ve cloned only the 3.0 branch of Laravel, resulting in roughly 10000 fewer objects to be downloaded.
By checking the contents of git branch -a we can also verify that other branch info has not been fetched:
$ cd laravel
$ git branch -a
* 3.0
remotes/origin/3.0
~
Shallow Cloning + Single Branch Cloning
By combining both we can download only the latest files of a specific branch. Since the use of --single-branch is implied when using --depth, we can drop the former and our command will look like this:
Container Queries allow authors to style elements according to the size of a container. This is similar to a @media query, except that it evaluates against a container instead of the viewport.
/* (1) Create an implicit "container root" */
main,
aside {
contain: size;
}
.media-object {
display: grid;
gap: 1em;
}
/* (2) Container Query targeting the nearest
"container root" ancestor. The rules nested
inside will only be applied if the "container
root" has a max-width of 45em */
@container (max-width: 45em) {
.media-object {
grid-template: 'img content' auto / auto 1fr;
}
}
Applying contain: size;(1) onto an element will make it an implicit “container root” or “containment context”. Elements contained inside it can then have container queries applied onto them, by use of a new at-rule @container (<container-media-query>)(2). The target selector and CSS rules to apply in that case are β similar to what we do with βregularβ media queries β nested within the @container at-rule.
In the example above extra rules will be applied to .media-object whenever its nearest “container root” ancestor β such as <main> or <aside> β has a max-width of 45em.
~
A previous version of this proposal by L. David Baron required a context selector to be set, but that has been dropped here. The @container rule from Miriam’s version will work in any containment context (read: the nearest parent element that has contain: size set). The syntax might still change, but that’s irrelevant to the prototype which is to be implemented:
This is not at all finalized, but the underlying problems we need to solve in Blink are (mostly) the same regardless of how the feature is accessed, so we’ll for now use this proposal as the temporary syntax.
When double clicking a submit button your form will be sent twice. Using JavaScript you can prevent this from happening, but wouldn’t it be nice if this behavior could be tweaked by use of an attribute on the <form>? If you think so, feel free to give this issue a thumbs up.
Today Sebastian wondered:
Disabling a form submit button while submitting: yay or nay?
(I thought I saw some research that discourages it, but can't remember where or why)
I quickly chimed in saying that I do tend to lock up forms when submitting them. Let me explain why β¦
~
I started locking up submitted forms as users of the apps I’m building reported that sometimes the actions they had taken β such as adding an entry β were performed twice. I took me some time to figure out what exactly was going on, but eventually I found out this was because they were double clicking the submit button of the forms. As they double clicked the button, the form got sent over twice. By locking up forms after their first submission, all successive submissions β such as those triggered by that second click of a double click β are ignored.
~
To prevent these extra form submissions from being made I donβt hijack the form with Ajax nor do I perform any other complicated things. I let the inner workings of the web and forms in the browser be: when pressing the submit button the browser will still collect all form data, build a new HTTP request, and execute that request.
What I simply do is extend the form’s capabilities by adding a flag β by means of a CSS class β onto the form to indicate whether it’s being submitted or not. I can then use this flag’s presence to deny any successive submissions, and also hook some CSS styles on. β Progressive Enhancement, Yay! π
The code looks as follows:
// Prevent Double Submits
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', (e) => {
// Prevent if already submitting
if (form.classList.contains('is-submitting')) {
e.preventDefault();
}
// Add class to hook our visual indicator on
form.classList.add('is-submitting');
});
});
π‘ Although the problem initially was a double click problem, we don’t listen for any clicks on the submit button but listen for the form’s submit event. This way our code not only works when clicking any of the submit buttons, but also when pressing enter to submit.
With that .is-submitting class in place, we can then attach some extra CSS onto the form to give the user visual feedback. Here’s a few examples:
Note that this solution might not cover 100% of all possible scenarios, as it doesn’t take failing networks and other things that might go wrong into account. However, as I’m relying on the basic mechanisms of the web I think I can also rely on the browser to show that typical “Confirm Form Resubmission” interstitial should a timeout occur. Additionally, if need be, one could always unlock the form after a fixed amount of time. That way the user will be able to re-submit it again.
~
Dealing with double form submissions isn’t a new issue at all. You’ll find quite some results when running a few queries through Google β something I only found out after stumbling over the issue myself.
Back in 2015 (!) Mattias Geniar also pondered about this, after being pulled into the subject from a sysadmin view. Now, when even sysadmins are talking about an HTML/UX issue you know there’s something going on. This made me wonder why browsers behaved like that and how we could solve it:
π€ Why is it that browsers don't prevent double form submissions by default? Some users (mistakingly) double click on submit buttons.
π‘ An attribute on <form> to tweak this behavior β instead of having to rely on JavaScript β would come in handy β¦
An attribute on <form> to tweak this behavior β instead of having to rely on JavaScript β would come in handy and form a nice addition to the spec.
I see two options to go forward:
Browsers/the standard keeps the current behavior and allow multiple submits. Developers must opt-in to prevent multiple submissions using a preventmultiplesubmits attribute.
Browsers/the standard adjust the current behavior to only submit forms once. Developers must opt-in to allow multiple submissions using a allowmultiplesubmits attribute.
Initial response on the issue was very low, and it looks like this isn’t that big of a priority.
Back then I was more in favor of the second solution, but now I’m quite undecided as changing the default behavior β which has been around for ages β is quite tricky.
~
Another way that this issue could be fixed is at the browser level: if they were to treat double clicks on form submit buttons as single clicks, then the double click problem β but not the double submit problem β could also be taken care of.
To then attach styles to forms being submitted a CSS Pseudo Class :submitting would come in handy. Come to think of it, this Pseudo Class would be a quite nice addition to CSS in itself, no matter whether this double click issue gets solved or not.
~
Winging back to the addition to the HTML spec I suggested: If you do think it could be something the HTML spec could benefit from, feel free to add a thumbs up to the issue to indicate that you want this, or add an extra comment to it if you have more input on the subject.
Did this help you out? Like what you see? Thank me with a coffee.
I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!
In a project we at vBridge are working on, we rely on a Virtual Private Network to link our connected devices, certain servers, and our webapp together. I had an issue where a specific server in the 10.55/24 range was nog being reachable.
While debugging the issue β going deeper into the rabbit hole called the 5 Whys β I eventually needed to verify if the proper routing tables from the VPN connection had been set up or not. To do so, I used netstat:
Cloud Run is a fully managed compute platform by Google that automatically scales stateless containers. By abstracting away all infrastructure management, us developers can focus on what matters most: building great applications.
In this talk I’ll show you not only how to deploy PHP/Node applications onto Cloud Run, but also how to create a build pipeline using either Google Cloud Build or Github Actions.
In mid August the video got released, which I’ve embedded below:
Thanks to the organisers for having me, and thanks to the attendees for coming to see me. I hope you all had fun attending this talk. I know I had making it (and whilst bringing it forward) π
An ECMAScript Language Feature that I’m looking forward to is Declarations in Conditionals. It adds the capability to declare variables inside conditional statements. Let’s take a look β¦
~
Setting the Scene
Say you want to iterate over an array which might be tucked away inside an object. To do so, you’d most likely first check for that variable’s existence using a simple if
if (user.meta.interests) {
for (let interest of user.meta.interests) {
// …
}
}
As we’re now typing user.meta.interests twice in our code you might declare an intermediary variable, and use that further down in your code:
let interests = user.meta.interests;
if (interests) {
for (let interest of interests) {
// …
}
}
But now you have a nasty side-effect: you’re loitering the scope with this extra interests variable. This is quite useless, as you’re only using it inside the if statement.
πββοΈ To make it totally failsafe, you might also want to add some Optional Chaining in there and check if interests is iterable, but that’s beyond the scope of this post.
~
Hello “Declarations in Conditionals”
With the “Declarations in Conditionals” proposal we can keep this extra interests variable, yet without loitering the outer scope. We do this by declaring the variable directly inside the conditional (e.g. if statement), like so:
if (let interests = user.meta.interests) {
for (let interest of interests) {
// …
}
}
β οΈ Note that this is an assignment right there, not an equality check!
With that one line the interests variable:
will be declared
will have a value assigned to it
will be checked for being falsy or not
Scope-wise the interests variable will have its scope limited local to the conditional (e.g. only visible inside the if).
The proposal limits declarations to use either const or let; declarations in conditionals with var are currently not allowed.
π‘ You might not 100% realize it, but you’re already doing this kind of thing when using a for loop. Inside its initialization part (e.g. its first part) you are also declaring variables.
for (let i = 0; i < 5; i++) {
console.log(i);
}
βοΈ Did you know that you can declare more than one variable there, separated by a comma? In the example below I declare an extra variable len right after declaring i:
const cars = ['Ford', 'Mercedes', 'BMW'];
for (let i = 0, len = cars.length; i < len; i++) {
console.log(cars[i]);
}
π€ What about the scope of len you might ask? Variables declared using let are scoped to the statement (e.g. local to the loop). Variables declared using var get hoisted. And oh, it’s not allowed to declare variables using const here.
~
So, when can we use this?
The proposal is currently Stage-1, so it still has a long way to go before it can become an official part of the standard β if it ever will be.
Stage-1 is the Proposal stage. It signals that the committee is showing interest in the proposal and that it is seeking further investigation on how to tackle it. At this stage the proposal is subject to heavy changes. It is only when a proposal reaches Stage 4 that it is ready to become part of the ECMAScript Specification.
As it’s still Stage-1, it can change quite a lot before it to be finished. And I’m quite sure it will change, as right now:
It only allows declaring one variable.
It only checks for values being falsy or not.
These two feature are currently listed under “Future Work” of the proposal, and it looks like this:
if (let x = 1, y = 2; x || y) {
// β¦
}
Must say I’m pretty curious how this one will evolve, as I’m already quite excited about it: it’s one of those small additions that will have a pretty big impact on how I write my code.
On the Full Stack Belgium Slack channel, user @Brammm recently asked how to create a simple image gallery.
Anyone have a favorite way of making an image grid with CSS? Iβd like one of those βfancyβ ones where no matter the aspect ratio of the image, they all have the same gap between them.
While some suggested existing solutions such as React Photo Gallery or photo-stream, I focussed on only the layout-question and suggested a DIY solution using display: grid; and object-fit: cover;.
Using only those those two props it’s really easy to create a grid based gallery.
Using display: grid; you create a grid layout. I decided to create a grid of square cells/tiles.
As I stretch out each image inside a tile, the images can get deformed. Using object-fit: cover; this can be prevented. Note that you will visually lose some data, as the edges will get clipped, but that was not a concern to @Brammm.
~
Proof Of Concept
Using photos from the birth of my son Noah I knocked up a small small proof of concept in about 10 minutes. Tweaking it a bit more β by adding some CSS Variables into the mix β I eventually landed on this:
To make things visually more interesting, and also since some photos are portrait and some landscape, the size of the tiles must differ a bit. With CSS Grid in place it’s really easy to stretch out cells so that they span more than one column or row:
With these in place, my gallery started to look like this:
There, looks good, right? π
π I know, I cheated a bit as I added the .wide/.high classes onto carefully selected tiles, leaving no gaps in my grid. To workaround potential gaps, one can always use grid-auto-flow: dense;, or use the (still under development)Grid Masonry from the Grid Level 2 spec. Note that in those cases the order of the images will differ from their source order.
~
Going further: Zoom / Lightbox functionality
Where I had originally stopped working on the gallery after that, today I wondered if I could adjust it a bit further and give it the ability to show the images at full screen, in some sort of Lightbox. Still using only CSS it’s possible to show an overlay while pressing+holding a tile.
Using the :active pseudo-selector you can know which element is currently being pressed
Using position: fixed; you can put content on top of the entire viewport
Combining the two as follows β¦
ul.gallery > li:active > img {
position: fixed; /* Position the image on top of the viewport contents */
z-index: 11;
top: 0;
right: 0;
bottom: 0;
left: 0;
object-fit: contain; /* Make sure the image isn't distorted */
padding: 1vw; /* Add some padding to make it breathe a bit */
background: rgba(0, 0, 0, 0.8); /* Dim the background */
}
While the version above does work (on Desktop), there’s a huge problem with it: it’s not (keyboard) accessible at all. In order to give the browser β and therefore user β hints about stuff being clickable we could add a truckload of ARIA attributes or simply use β¦ links. An extra benefit of this approach is that we then start using CSS :target, and eventually create Pure CSS Lightbox.
It’s not entirely perfect though, when using only the keyboard to navigate, you have to do some trickery to close the Lightbox: after zooming in on an image you’ll have to hit TAB to focus the close link and then hit ENTER to activate it β If only autofocus where available on non-inputs β¦
As an extra I also added some JS to make the β, β, and ESC keys work, so that it behaves like a βrealβ Lightbox. There’s plenty more things I could add to it, but that’s something way beyond the original goal of this post so I left it at that.
~
Did this help you out? Like what you see? Thank me with a coffee.
I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!
In a Flux query I was writing I wanted to negate the fetched values using a map function to multiply each value by -1:
map(fn: (r) => ({ _value: r._value * -1 }))
To my surprise this yielded an error:
type conflict: int != float
Took me a few Google Search Coupons to realize the error got trigger not by the type of the _value, but by the -1 in there. The solution therefore is really simple: don’t multiply by an integer (e.g. -1) but multiply by a float (e.g. -1.0); like so:
map(fn: (r) => ({ _value: r._value * -1.0 }))
π
Did this help you out? Like what you see? Thank me with a coffee.
I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!