That’s because the variable is recognized as a string and what we need is a number that can be interpolated between two numeric values. That’s where we can call on @property to not only register the variable as a custom property, but define its syntax as a number:
You’re going to want to check browser support since @property has only landed in Chrome (starting in version 85) as of this writing. And if you’re hoping to sniff it out with @supports, we’re currently out of luck because it doesn’t accept at-rules as values… yet. That will change once at-rule()becomes a real thing.
Normally, a project will have a set of pre-determined font sizes, usually as variables named in such a way that seeks some semblance of order and consistency. Any project of considerable size can use something like that. There are always headings, paragraphs, lists, etc. You could set font sizes explicitly and directly everywhere (e.g. font-size: 18px). Raw CSS, as it were. I do see that occasionally — mixing not just sizes but also units like px, rem and em in mindless chaos.
That’s why the CSS of a project typically uses variables or mixins — we’re shooting for structure, maintainability and, ultimately, consistency. We all know naming is hard and it doesn’t take looking much further than naming font size variables to see why. How should we name a small font size variable so it’s clear that it’s smaller than a large font size variable? And what happens if we need to insert a new variable in between them — is that one named in a way that clearly explains its relationship to the other size variables?
We’ll continue talking about naming font size variables in this post. But, really, the issue extends beyond font sizes to any sort of size or length value. Think paddings, margins, widths, heights, border radii, etc. These things need structure and consistency, too!
How do you define font sizes in your project? Does it look something like this with custom variables:
Or perhaps in Sass (which is what we’ll be using throughout this article) you might have variables for $ small, $ medium, and $ large font sizes.
Fine. After a while, let’s say the designer adds a new <h1> heading for a hero section. And it is very large. Larger than anything you have in the project. No problem, you reply. You add an $ xlarge to the project, and go about your day.
The following day, the designer makes a nice form label, which again has a new font size. This new size, though, is larger than small, but smaller than medium.
Here we go.
What should you call it? $ small-medium? $ small-2? $ smedium? Whatever you name it, you won’t be happy with it. Because there is no word for that.
Or should you maybe refactor it? Create a new $ xsmall, and change all instances of $ small to $ xsmall? And then you can use $ small for the form label? There’s a small risk that you will forget to change somewhere and, hey, presto: a bug. What happens next time, when something is introduced that has a larger size than the $ medium variable value? Do we have to refactor $ large and $ xlarge too?
I suggest adhering to a scale, always. An easy fix would be to further abstraction, perhaps ditching numbers and sizes in favor of functional names, like $ form-label instead of $ small-2 or $ xsmall.
That is a broken scale. It’s a size concept and a component concept mixed together. It raises questions. Should an alert or a button be allowed to use $ form-label? Yuck.
Maybe you have a Greek thing going on, naming the variables $ alpha, $ beta, $ gamma? Let me ask you then, what is then bigger than $ alpha? $ alpha-large? Or wait, is $ alpha the small one?
I have also seen names like $ button-font-size, $ label-font-size, $ blockquote-font-size. That seems to me like one variable per element used, instead of a scale, and sounds like it could be a lot of duplicated code if the same value is being used in multiple places, but with different names.
Perhaps you’re working with one base font size and percentages only? Sure, but I would say you need variables for the percentages. That’s how Geoff handles font sizing and even he admits that the setup raises his own eyebrows. Calculations with subjectively-named variables might be clear to you, but crazy-looking and complicated for anyone else jumping into the project.
Adding and removing stuff constantly is the way we want to work. This is modern day development — MVP, Agile, and all the other hot buzzwords.
What we need is a scale syntax that allows room for changes. Adding a new size to the scale should be easy without introducing breaking changes. I’m thinking of a kind of scale that is both flexible and infinite. It must be more sophisticated than $ small, $ medium and $ large.
It should be also be descriptive and intuitive. Preferably, you shouldn’t have to look up the variable names in the settings file or the config, or wherever you store these things. I don’t have the slightest clue if $ epsilon comes before or after $ sigma. Do you?
Using existing systems
Before trying to invent something new, is there an existing syntax or system we can leverage? Here are a few I’ve encountered.
Surely, you’re familiar with terms like “kilobyte” and “megabyte.” Europeans are very used to “millimeter” and “centimeter.” Other examples are “giga,” “tera,” and “peta.” These prefixes can be used for length, weight, volume and more. Could a $ centi font size work? It is intuitive to a certain extent, that is, if you’re familiar with the metric system. This is a finite scale. And there’s no room to add new sizes because they are already set.
Long before computers and desktop publishing, books and newspapers were printed with lead type. The type setters had different names for different sizes. The sizes have a reference to a point size (pt) and could, in theory, be used for pixel sizes (px).
The type sizes in this system are called “Nonpareil,” “Pica,” “Cicero,” and “Great Primer,” just to name a few. The names are different depending on continent and country. Plus, the same name can have different sizes, so… quite confusing.
That said, I do like this system in a way because it would be like paying respect to an old craftsmanship from times past. But the names are so weird and specifically meant for type sizing, that it feels like a stretch to use for things like breakpoints and spacing.
Placing everyday objects on a scale
How about using stuff from our everyday life? Say chili peppers.
There are many kinds of chili peppers. The $ habanero, is hotter than the $ cayenne, which is hotter than the $ jalapeno. That would be fun, yeah?
But as much as I enjoy the idea of writing font-size: $ tabasco, I see two problems. If you’re not into peppers, you cannot know which pepper is hotter than another pepper — so, it’s not universally intuitive. Also, the bell pepper is 0 on the Scoville scale, and nothing is below that. Carolina Reaper is the hottest pepper in the world, so the scale is finite.
And yeah, peppers scale-wise are not larger or smaller, they are hotter. Bad concept. Maybe something more common, like types of balls?
There‘s a large range of different kinds of balls. You have handballs, soccer balls, volleyballs, etc. Need something larger than a medicine ball? Use $ beach. Something smaller than a tennis ball? Use $ pingpong. This is very intuitive, as I’d imagine everyone has played with all sorts of balls at some point, or at least are familiar of them from sports.
But is a ping pong ball larger than a golf ball? Who knows? Further, a bowling ball and a soccer ball are actually the same size. So… again, not perfect.
Scaling up to planets could work, but you would have to be knowledgeable in astronomy.
How about straight-up numbers? We’re unable to use numbers alone because tools like stylelinter will protest. But would something like this work:
Well, it’s infinite as there is always room for new additions. But it’s also incredibly specific, and there are some downsides to have the actual value be part of the name like that. Let’s assume that $ font-18 is used in a lot of places. And now, they say, all places with 18px must be changed to 19px (because reasons). Now we need to rename the variable from $ font-18 to $ font-19 then change the value of $ font-19 from 18px to 19px. And that’s before we finally update all places using $ font-18 to $ font-19. This is almost like using raw CSS. Low score for maintainability.
What about the animal kingdom?
Mother Nature has provided a myriad of species on this earth, which comes in handy in this situation. Imagine something like this:
$ mouse: 12px; $ dog: 16px; $ hippo: 24px;
Need something smaller than a mouse? Use $ bee, $ ant or $ flea. Larger than a bear? Try $ moose or $ hippo. Larger than an elephant? Well, you have the $ whale, or heck, we can go prehistoric and use $ t-rex. There’s always an animal to squeeze in here. Very versatile, very intuitive, also infinite (almost). And fun, too — I wouldn’t mind doing font-size: $ squirrel. 🤩
But then again, even that might require needing to reference the variables, unless we know exactly which animals are contained in our zoo of font sizes. But maybe that’s not a big deal as long as it scales.
I have spent way too much time pondering this
Or have I? The code base is where you spend your working hours. It’s your work environment, just like chairs and monitors. And the workplace should be a nice place.
How do you handle your font size scales? Do you have one system for fonts and another for things like margins? Can anyone jump right into your code and understand how everything is organized? Please tell in the comments!
If you’re anything like me, you like being lazyshortcuts. The “Deploy to Netlify” button allows me to take this lovely feature of my personality and be productive with it.
Clicking the button above lets me (or you!) instantly clone my Next.js starter project and automatically deploy it to Netlify. Wow! So easy! I’m so happy!
Now, as I was perusing the docs for the button the other night, as one does, I noticed that you can pre-fill environment variables to the sites you deploy with the button. Which got me thinking… what kind of sites could I customize with that?
Idea: “Link in Bio” website
Ah, the famed “link in bio” you see all over social media when folks want you to see all of their relevant links in life. You can sign up for the various services that’ll make one of these sites for you, but what if you could make one yourself without having to sign up for yet another service?
But, we also are lazy and like shortcuts. Sounds like we can solve all of these problems with the “Deploy to Netlify” (DTN) button, and environment variables.
How would we build something like this?
In order to make our DTN button work, we need to make two projects that work together:
A template project (This is the repo that will be cloned and customized based on the environment variables passed in.)
A generator project (This is the project that will create the environment variables that should be passed to the button.)
I decided to be a little spicy with my examples, and so I made both projects with Vite, but the template project uses React and the generator project uses Vue.
I’ll do a high-level overview of how I built these two projects, and if you’d like to just see all the code, you can skip to the end of this post to see the final repositories!
The Template project
To start my template project, I’ll pull in Vite and React.
npm init @vitejs/app
After running this command, you can follow the prompts with whatever frameworks you’d like!
Now after doing the whole npm install thing, you’ll want to add a .local.env file and add in the environment variables you want to include. I want to have a name for the person who owns the site, their profile picture, and then all of their relevant links.
VITE_NAME=Cassidy Williams VITE_PROFILE_PIC=https://github.com/cassidoo.png VITE_GITHUB_LINK=https://github.com/cassidoo VITE_TWITTER_LINK=https://twitter.com/cassidoo
You can set this up however you’d like, because this is just test data we’ll build off of! As you build out your own application, you can pull in your environment variables at any time for parsing with import.meta.env. Vite lets you access those variables from the client code with VITE_, so as you play around with variables, make sure you prepend that to your variables.
Ultimately, I made a rather large parsing function that I passed to my components to render into the template:
function getPageContent() { // Pull in all variables that start with VITE_ and turn it into an array let envVars = Object.entries(import.meta.env).filter((key) => key[0].startsWith('VITE_')) // Get the name and profile picture, since those are structured differently from the links const name = envVars.find((val) => val[0] === 'VITE_NAME')[1].replace(/_/g, ' ') const profilePic = envVars.find((val) => val[0] === 'VITE_PROFILE_PIC')[1] // ... // Pull all of the links, and properly format the names to be all lowercase and normalized let links = envVars.map((k) => { return [deEnvify(k[0]), k[1]] }) // This object is what is ultimately sent to React to be rendered return { name, profilePic, links } } function deEnvify(str) { return str.replace('VITE_', '').replace('_LINK', '').toLowerCase().split('_').join(' ') }
I can now pull in these variables into a React function that renders the components I need:
And voilà! With a little CSS, we have a “link in bio” site!
Now let’s turn this into something that doesn’t rely on hard-coded variables. Generator time!
The Generator project
I’m going to start a new Vite site, just like I did before, but I’ll be using Vue for this one, for funzies.
Now in this project, I need to generate the environment variables we talked about above. So we’ll need an input for the name, an input for the profile picture, and then a set of inputs for each link that a person might want to make.
In my App.vue template, I’ll have these separated out like so:
So in this component, there’s the two inputs that are adding to an object called newItem, and then the ListItem component lists out all of the links that have been created already, and each one can delete itself.
Now, we can take all of these values we’ve gotten from our users, and populate the GenerateButton component with them to make our DTN button work!
The template in GenerateButton is just an <a> tag with the link. The power in this one comes from the methods in the <script>.
// ... methods: { convertLink(str) { // Convert each string passed in to use the VITE_WHATEVER_LINK syntax that our template expects return `VITE_$ {str.replace(/ /g, '_').toUpperCase()}_LINK` }, convertListOfLinks() { let linkString = '' // Pass each link given by the user to our helper function this.list.forEach((l) => { linkString += `$ {this.convertLink(l.name)}=$ {l.url}&` }) return linkString }, // This function pushes all of our strings together into one giant link that will be put into our button that will deploy everything! siteLink() { return ( // This is the base URL we need of our template repo, and the Netlify deploy trigger 'https://app.netlify.com/start/deploy?repository=https://github.com/cassidoo/link-in-bio-template#' + 'VITE_NAME=' + // Replacing spaces with underscores in the name so that the URL doesn't turn that into %20 this.name.replace(/ /g, '_') + '&' + 'VITE_PROFILE_PIC=' + this.propic + '&' + // Pulls all the links from our helper function above this.convertListOfLinks() ) }, },
Believe it or not, that’s it. You can add whatever styles you like or change up what variables are passed (like themes, toggles, etc.) to make this truly customizable!
Put it all together
Once these projects are deployed, they can work together in beautiful harmony!
This is the kind of project that can really illustrate the power of customization when you have access to user-generated environment variables. It may be a small one, but when you think about generating, say, resume websites, e-commerce themes, “/uses” websites, marketing sites… the possibilities are endless for turning this into a really cool boilerplate method.
In the news this week, Firefox gets rounded outlines, SVG animations are now GPU-accelerated in Chrome, there are no physical units in CSS, The New York Times crossword is accessible, and CSS variables are resolved before the value is inherited.
Let’s jump in the news!
Rounded outlines are coming to Firefox
The idea to have the outline follow the border curve has existed ever since it became possible to create rounded borders via the border-radius property in the mid 2000s. It was suggested to Mozilla, WebKit, and Chromium over ten years ago, and it’s even been part of the CSS UI specification since 2015:
The parts of the outline are not required to be rectangular. To the extent that the outline follows the border edge, it should follow the border-radius curve.
Fast-forward to today in 2021 and outlines are still rectangles in every browser without exception:
But this is finally starting to change. In a few weeks, Firefox will become the first browser with rounded outlines that automatically follow the border shape. This will also apply to Firefox’s default focus outline on buttons.
Please star Chromium Issue #81556 (sign in required) to help prioritize this bug and bring rounded outlines to Chrome sooner rather than later.
SVG animations are now GPU-accelerated in Chrome
Until recently, animating an SVG element via CSS would trigger repaint on every frame (usually 60 times per second) in Chromium-based browsers. Such constant repainting can have a negative impact on the smoothness of the animation and the performance of the page itself.
The latest version of Chrome has eliminated this performance issue by enabling hardware acceleration for SVG animations. This means that SVG animations are offloaded to the GPU and no longer run on the main thread.
In this example, the SVG circle is continuously faded in and out via a CSS animation (see code)
The switch to GPU acceleration automatically made SVG animations more performant in Chromium-based browsers (Firefox does this too), which is definitely good news for the web:
Hooray for more screen reader-accessible, progressively enhanced SVG animations and less Canvas.
There cannot be real physical units in CSS
CSS defines six physical units, including in (inches) and cm (centimeters). Every physical unit is in a fixed ratio with the pixel unit, which is the canonical unit. For example, 1in is always exactly 96px. On most modern screens, this length does not correspond to 1 real-world inch.
The FAQ page of the CSS Working Group now answers the question why there can’t be real physical units in CSS. In short, the browser cannot always determine the exact size and resolution of the display (think projectors). For websites that need accurate real-world units, the Working Group recommends per-device calibration:
Have a calibration page, where you ask the user to measure the distance between two lines that are some CSS distance apart (say, 10cm), and input the value they get. Use this to find the scaling factor necessary for that screen (CSS length divided by user-provided length).
This scaling factor can then be set to a custom property and used to compute accurate lengths in CSS:
The Times crossword is accessible to screen reader users
The NYT Open team wrote about some of the improvements to the New York Times website that have made it more accessible in recent years. The website uses semantic HTML (<article>, <nav>, etc.), increased contrast on important components (e.g., login and registration), and skip-to-content links that adapt to the site’s paywall.
Furthermore, the Games team made the daily crossword puzzle accessible to keyboard and screen reader users. The crossword is implemented as a grid of SVG <rect> elements. As the user navigates through the puzzle, the current square’s aria-label attribute (accessible name) is dynamically updated to provide additional context.
The screen reader announces the clue, the number of letters in the solution, and the position of the selected square
You can play the mini crossword without an account. Try solving the puzzle with the keyboard.
CSS variables are resolved before the value is inherited
Yuan Chuan recently shared a little CSS quiz that I didn’t answer correctly because I wasn’t sure if a CSS variable (the var() function) is resolved before or after the value is inherited. I’ll try to explain how this works on the following example:
html { --text-color: var(--main-color, black); } footer { --main-color: brown; } p { color: var(--text-color); }
The question: Is the color of the paragraph in the footer black or brown? There are two possibilities. Either (A) the declared values of both custom properties are inherited to the paragraph, and then the color property resolves to brown, or (B) the --text-color property resolves to black directly on the <html> element, and then this value is inherited to the paragraph and assigned to the color property.
The correct answer is option B (the color is black). CSS variables are resolved before the value is inherited. In this case, --text-color falls back to black because --main-color does not exist on the <html> element. This rule is specified in the CSS Variables module:
It is important to note that custom properties resolve any var() functions in their values at computed-value time, which occurs before the value is inherited.
The other day I was working on a blog where each post has a custom color attached to it for a little dose of personality. The author gets to pick that color in the CMS when they’re writing the post. Just a super-light layer of art direction.
To make that color show up on the front end, I wrote the value right into an inline style attribute on the <article> element. My templates happened to be in Liquid, but this would look similar in other templating languages:
{% for post in posts %} <article style="background: {{post.custom_color}}"> <h1>{{post.title}}</h1> {{content}} </article> {% endfor %}
No problem there. But then I thought, “Wouldn’t it be nice if the custom color only showed up when when hovering over the article card?” But you can’t write hover styles in a style attribute, right?
My first idea was to leave the style attribute in place and write CSS like this:
Eventually, I decided I could use a style attribute to get the color value from the CMS, but instead of applying it right away, store it as a CSS variable:
Now that the color value is saved as a CSS variable, there are all kinds of other things we can do with it. For instance, we could make all links in the post appear in the custom color:
article a { color: var(--custom_color); }
And because the variable is scoped to the <article> element, it won’t affect anything else on the page. We can even display multiple posts on the same page where each one renders in its own custom color.
Browser support for CSS variables is pretty deep, with the exception of Internet Explorer. Anyway, just a neat little trick that might come in handy if you find yourself working with light art direction in a CMS, as well as a reminder of just how awesome CSS variables can be.
Turns out you can use several different libraries to pass color information around components. Or, you could use custom properties, built right into CSS, have no decline in your own developer experience, and deliver a faster experience to your users. Kent proves it here, with demos.
For the record, you could go a step further than Kent has here and not use CSS-in-JS at all, but still be updating CSS custom properties from button clicks in React and managing the state there and such. I’m telling ya, one of the main jobs of a UI component library like React is managing state, and CSS might as well know about that state so you can use it to do any styling you need to do.
Wait, not use CSS-in-JS? Kent:
I’ve never been so productive working with CSS than when I added a real programming language to it.
Extreme side eye, Kent.
We should be calling it CSS-in-React, also, since React is the only major framework that doesn’t have a blessed solution for styling.
Eleventy is increasing in popularity because it allows us to create nice, simple websites, but also — because it’s so developer-friendly. We can build large-scale, complex projects with it, too. In this tutorial we’re going to demonstrate that expansive capability by putting a together a powerful and human-friendly environment variable solution.
What are environment variables?
Environment variables are handy variables/configuration values that are defined within the environment that your code finds itself in.
For example, say you have a WordPress site: you’re probably going to want to connect to one database on your live site and a different one for your staging and local sites. We can hard-code these values in wp-config.php but a good way of keeping the connection details a secret and making it easier to keep your code in source control, such as Git, is defining these away from your code.
Here’s a standard-edition WordPress wp-config.php snippet with hardcoded values:
Using the same example of a wp-config.php file, we can introduce a tool like phpdotenv and change it to something like this instead, and define the values away from the code:
A way to define these environment variable values is by using a .env file, which is a text file that is commonly ignored by source control.
We then scoop up those values — which might be unavailable to your code by default, using a tool such as dotenv or phpdotenv. Tools like dotenv are super useful because you could define these variables in an .env file, a Docker script or deploy script and it’ll just work — which is my favorite type of tool!
The reason we tend to ignore these in source control (via .gitignore) is because they often contain secret keys or database connection information. Ideally, you want to keep that away from any remote repository, such as GitHub, to keep details as safe as possible.
Getting started
For this tutorial, I’ve made some starter files to save us all a bit of time. It’s a base, bare-bones Eleventy site with all of the boring bits done for us.
Step one of this tutorial is to download the starter files and unzip them wherever you want to work with them. Once the files are unzipped, open up the folder in your terminal and run npm install. Once you’ve done that, run npm start. When you open your browser at http://localhost:8080, it should look like this:
Also, while we’re setting up: create a new, empty file called .env and add it to the root of your base files folder.
Creating a friendly interface
Environment variables are often really shouty, because we use all caps, which can get irritating. What I prefer to do is create a JavaScript interface that consumes these values and exports them as something human-friendly and namespaced, so you know just by looking at the code that you’re using environment variables.
Let’s take a value like HELLO=hi there, which might be defined in our .env file. To access this, we use process.env.HELLO, which after a few calls, gets a bit tiresome. What if that value is not defined, either? It’s handy to provide a fallback for these scenarios. Using a JavaScript setup, we can do this sort of thing:
require('dotenv').config(); module.exports = { hello: process.env.HELLO || 'Hello not set, but hi, anyway 👋' };
What we are doing here is looking for that environment variable and setting a default value, if needed, using the OR operator (||) to return a value if it’s not defined. Then, in our templates, we can do {{ env.hello }}.
Now that we know how this technique works, let’s make it happen. In our starter files folder, there is a directory called src/_data with an empty env.js file in it. Open it up and add the following code to it:
require('dotenv').config(); module.exports = { otherSiteUrl: process.env.OTHER_SITE_URL || 'https://eleventy-env-vars-private.netlify.app', hello: process.env.HELLO || 'Hello not set, but hi, anyway 👋' };
Because our data file is called env.js, we can access it in our templates with the env prefix. If we wanted our environment variables to be prefixed with environment, we would change the name of our data file to environment.js . You can read more on the Eleventy documentation.
We’ve got our hello value here and also an otherSiteUrl value which we use to allow people to see the different versions of our site, based on their environment variable configs. This setup uses Eleventy JavaScript Data Files which allow us to run JavaScript and return the output as static data. They even support asynchronous code! These JavaScript Data Files are probably my favorite Eleventy feature.
Now that we have this JavaScript interface set up, let’s head over to our content and implement some variables. Open up src/index.md and at the bottom of the file, add the following:
Here’s an example: The environment variable, HELLO is currently: “{{ env.hello }}”. This is called with {% raw %}{{ env.hello }}{% endraw %}.
Pretty cool, right? We can use these variables right in our content with Eleventy! Now, when you define or change the value of HELLO in your .env file and restart the npm start task, you’ll see the content update.
Your site should look like this now:
You might be wondering what the heck {% raw %} is. It’s a Nunjucks tag that allows you to define areas that it should ignore. Without it, Nunjucks would try to evaluate the example {{ env.hello }} part.
Modifying image base paths
That first example we did was cool, but let’s really start exploring how this approach can be useful. Often, you will want your production images to be fronted-up with some sort of CDN, but you’ll probably also want your images running locally when you are developing your site. What this means is that to help with performance and varied image format support, we often use a CDN to serve up our images for us and these CDNs will often serve images directly from your site, such as from your /images folder. This is exactly what I do on Piccalilli with ImgIX, but these CDNs don’t have access to the local version of the site. So, being able to switch between CDN and local images is handy.
The solution to this problem is almost trivial with environment variables — especially with Eleventy and dotenv, because if the environment variables are not defined at the point of usage, no errors are thrown.
Open up src/_data/env.js and add the following properties to the object:
We’re using a default value for imageBase of /images/ so that if IMAGE_BASE is not defined, our local images can be found. We don’t do the same for imageProps because they can be empty unless we need them.
Open up _includes/base.njk and, after the <h1>{{ title }}</h1> bit, add the following:
<img src="https://assets.codepen.io/174183/mountains.jpg?width=1275&height=805&format=auto&quality=70" alt="Some lush mountains at sunset" />
By default, this will load /images/mountains.jpg. Cool! Now, open up the .env file and add the following to it:
Powering private and premium content with Eleventy
We can also use environment variables to conditionally render content, based on a mode, such as private mode. This is an important capability for me, personally, because I have an Eleventy Course, and CSS book, both powered by Eleventy that only show premium content to those who have paid for it. There’s all-sorts of tech magic happening behind the scenes with Service Workers and APIs, but core to it all is that content can be conditionally rendered based on env.mode in our JavaScript interface.
Let’s add that to our example now. Open up src/_data/env.js and add the following to the object:
mode: process.env.MODE || 'public'
This setup means that by default, the mode is public. Now, open up src/index.md and add the following to the bottom of the file:
{% if env.mode === 'private' %} ## This is secret content that only shows if we’re in private mode. This is called with {% raw %}`{{ env.mode }}`{% endraw %}. This is great for doing special private builds of the site for people that pay for content, for example. {% endif %}
If you refresh your local version, you won’t be able to see that content that we just added. This is working perfectly for us — especially because we want to protect it. So now, let’s show it, using environment variables. Open up .env and add the following to it:
MODE=private
Now, restart Eleventy and reload the site. You should now see something like this:
You can run this conditional rendering within the template too. For example, you could make all of the page content private and render a paywall instead. An example of that is if you go to my course without a license, you will be presented with a call to action to buy it:
Fun mode
This has hopefully been really useful content for you so far, so let’s expand on what we’ve learned and have some fun with it!
I want to finish by making a “fun mode” which completely alters the design to something more… fun. Open up src/_includes/base.njk, and just before the closing </head> tag, add the following:
This code is using the same logic and rendering a fun banner if funMode is true. Let’s create our environment variable interface for that now. Open up src/_data/env.js and add the following to the exported object:
funMode: process.env.FUN_MODE
If funMode is not defined, it will act as false, because undefined is a falsy value.
Next, open up your .env file and add the following to it:
FUN_MODE=true
Now, restart the Eleventy task and reload your browser. It should look like this:
Pretty loud, huh?! Even though this design looks pretty awful (read: rad), I hope it demonstrates how much you can change with this environment setup.
Wrapping up
We’ve created three versions of the same site, running the same code to see all the differences:
All of these sites are powered by identical code with the only difference between each site being some environment variables which, for this example, I have defined in my Netlify dashboard.
I hope that this technique will open up all sorts of possibilities for you, using the best static site generator, Eleventy!
The main idea is simple: You write your main rule using CSS variables, and then use :nth-of-*() rules to set these variables to something different every N items. If you use enough variables, and choose your Ns for them to be prime numbers, you reach a good appearance of pseudo-randomness with relatively small Ns.
The update to the trick is that she doesn’t update the whole block of code for each of the selectors, it’s just one part of the clipping path that gets updated via a custom property.
The title of this Sara Soueidan article speaks to me. I’m a big fan of the idea that some CSS is best applied globally, and some CSS is best applied scoped to a component. I’m less interested in how that is done and more interested in just seeing that conceptual approach used in some fashion.
Sara details an approach where components don’t have too much styling by default, but have CSS custom properties applied to them that are ready to take values should you choose to set them.
For each pattern, I’ve found myself modifying the same properties whenever I needed to use it — like the font, colors (text, background, border), box shadow, spacing, etc. So I figured it would be useful and time-saving if I created variables for those properties, define those variables in the ‘root’ of the component, and ‘pass in’ the values for these variables when I use the pattern as I need. This way I can customize or theme the component by changing the property values in one rule set, instead of having to jump between multiple ones to do so.
JavaScript (or ECMAScript) is the programming language that powers the web. Created in May 1995 by Brendan Eich, it’s found its place as a widely-used and versatile technology. Despite its success, it’s been met with its fair share of criticism, especially for idiosyncrasies. Things like objects being casted to string form when used as indices, 1 == "1" returning true, or the notoriously confusing this keyword. A particularly interesting quirk though, is the existence of various techniques for variable privacy.
In its current state, there is no “direct” way to create a private variable in JavaScript. In other languages, you can use the private keyword or double-underscores and everything works, but variable privacy in JavaScript carries characteristics that make it seem more akin to an emergent trait of the language rather than an intended functionality. Let’s introduce some background to our problem.
The “var” keyword
Before 2015, there was essentially one way to create a variable, and that was the var keyword. var is function-scoped, meaning that variables instantiated with the keyword would only be accessible to code within the function. When outside of a function, or “global” essentially, the variable will be accessible to anything executed after the definition of the variable. If you try to access the variable in the same scope before its definition, you will get undefined rather than an error. This is due to the way the var keyword “hoists.”
// Define "a" in global scope var a = 123; // Define "b" in function scope (function() { console.log(b); //=> Returns "undefined" instead of an error due to hoisting. var b = 456; })(); console.log(a); // => 123 console.log(b); // Throws "ReferenceError" exception, because "b" cannot be accessed from outside the function scope.
The birth of ES6 variables
In 2015, ES6/ES2015 was made official, and with it came two new variable keywords: let and const. Both were block-scoped, meaning that variables created with the keywords would be accessible from anything within the same pair of braces. Same as with var, but the let and const variables could not be accessed outside of block scope with loops, functions, if statements, braces, etc.
const a = 123; // Block scope example #1 if (true) { const b = 345; } // Block scope example #2 { const c = 678; } console.log(a); // 123 console.log(b); // Throws "ReferenceError" because "b" cannot be accessed from outside the block scope. console.log(c); // Throws "ReferenceError" because "b" cannot be accessed from outside the block scope.
Since code outside of the scope cannot access the variables, we get an emergent trait of privacy. We’re going to cover some techniques for implementing it in different ways.
Using functions
Since functions in JavaScript also are blocks, all variable keywords work with them. In addition, we can implement a very useful design pattern called the “module.”
Any of a number of distinct but interrelated units from which a program may be built up or into which a complex activity may be analyzed.
—”Module” Definition 1.2
The module design pattern is very useful in JavaScript because it combines public and private components and it allows us to break a program into smaller components, only exposing what another part of the program should be able to access through a process called “encapsulation.” Through this method, we expose only what needs to be used and can hide the rest of the implementation that doesn’t need to be seen. We can take advantage of function scope to implement this.
const CarModule = () => { let milesDriven = 0; let speed = 0; const accelerate = (amount) => { speed += amount; milesDriven += speed; } const getMilesDriven = () => milesDriven; // Using the "return" keyword, you can control what gets // exposed and what gets hidden. In this case, we expose // only the accelerate() and getMilesDriven() function. return { accelerate, getMilesDriven } }; const testCarModule = CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
With this, we can get the number of miles driven, as well as the amount of acceleration, but since the user doesn’t need access to the speed in this case, we can hide it by only exposing the accelerate() and getMilesDriven() method. Essentially, speed is a private variable, as it is only accessible to code inside of the same block scope. The benefit to private variables begins to become clear in this situation. When you remove the ability to access a variable, function, or any other internal component, you reduce the surface area for errors resulting from someone else mistakenly using something that wasn’t meant to be.
The alternative way
In this second example, you’ll notice the addition of the this keyword. There’s a difference between the ES6 arrow function ( => ) and the traditional function(){}. With the function keyword, you can use this, which will be bound to the function itself, whereas arrow functions don’t allow any kind of use of the this keyword. Both are equally-valid ways to create the module. The core idea is to expose parts that should be accessed and leave other parts that should not be interacted with, hence both public and private data.
function CarModule() { let milesDriven = 0; let speed = 0; // In this case, we instead use the "this" keyword, // which refers to CarModule this.accelerate = (amount) => { speed += amount; milesDriven += speed; } this.getMilesDriven = () => milesDriven; } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
Enter ES6 Classes
Classes were another addition that came with ES6. Classes are essentially syntactic sugar — in other words, still a function, but potentially “sweetening” it into a form that’s easier to express. With classes, variable privacy is (as of now) close to impossible without making some major changes to the code.
One of the first things that stands out is that the milesDriven and speed variable are inside of a constructor() function. Note that you can also define the variables outside of the constructor (as shown in the code comment), but they are functionally the same regardless. The problem is that these variables will be public and accessible to elements outside of the class.
Let’s look at some ways to work around that.
Using an underscore
In cases where privacy is to prevent collaborators from making some catastrophic mistake, prefixing variables with an underscore (_), despite still being “visible” to the outside, can be sufficient to signal to a developer, “Don’t touch this variable.” So, for example, we now have the following:
// This is the new constructor for the class. Note that it could // also be expressed as the following outside of constructor(). /* _milesDriven = 0; _speed = 0; */ constructor() { this._milesDriven = 0; this._speed = 0; }
While this does work for its specific use case, it’s still safe to say that it’s less than ideal on many levels. You can still access the variable but you also have to modify the variable name on top of that.
Putting everything inside the constructor
Technically, there is a method for variable privacy in a class that you can use right now, and that’s placing all variables and methods inside the constructor() function. Let’s take a look.
class CarModule { constructor() { let milesDriven = 0; let speed = 0; this.accelerate = (amount) => { speed += amount; milesDriven += speed; } this.getMilesDriven = () => milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); // undefined -- We have true variable privacy now.
This method accomplishes true variable privacy in the sense that there is no way to directly access any variables that aren’t intentionally exposed. The problem is that we now have, well, code that doesn’t look all that great compared to what we had before, in addition to the fact that it defeats the benefits of the syntactic sugar we had with classes. At this point, we might as well be using the function() method.
Using WeakMap
There’s another, more creative way to go about making a private variable, and that’s using WeakMap(). Although it may sound similar to Map, the two are very different. While maps can take any type of value as a key, a WeakMap only take objects and deletes the values in the WeakMap when the object key is garbage collected. In addition, a WeakMap cannot be iterated through, meaning that you must have access to the reference to an object key in order to access a value. This makes it rather useful for creating private variables, since the variables are effectively invisible.
class CarModule { constructor() { this.data = new WeakMap(); this.data.set(this, { milesDriven: 0, speed: 0 }); } accelerate(amount) { // In this version, we instead create a WeakMap and // use the "this" keyword as a key, which is not likely // to be used accidentally as a key to the WeakMap. const data = this.data.get(this); const speed = data.speed + amount; const milesDriven = data.milesDriven + data.speed; this.data.set({ speed, milesDriven }); } this.getMilesDriven = () => this.data.get(this).milesDriven; } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.data); //=> WeakMap { [items unknown] } -- This data cannot be accessed easily from the outside!
This solution is good at preventing an accidental usage of the data, but it isn’t truly private, since it can still be accessed from outside the scope by substituting this with CarModule. In addition, it adds a fair amount of complexity to the mix and, therefore, isn’t the most elegant solution.
Using symbols to prevent collisions
If the intent is to prevent name collisions, there is a useful solution using Symbol. These are essentially instances that can behave as unique values that will never be equal to anything else, except its own unique instance. Here’s an example of it in action:
class CarModule { constructor() { this.speedKey = Symbol("speedKey"); this.milesDrivenKey = Symbol("milesDrivenKey"); this[this.speedKey] = 0; this[this.milesDrivenKey] = 0; } accelerate(amount) { // It's virtually impossible for this data to be // accidentally accessed. By no means is it private, // but it's well out of the way of anyone who would // be implementing this module. this[this.speedKey] += amount; this[this.milesDrivenKey] += this[this.speedKey]; } getMilesDriven() { return this[this.milesDrivenKey]; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); // => undefined -- we would need to access the internal keys to access the variable. Like the underscore solution, this method more or less relies on naming conventions to prevent confusion.
TC39 private class field proposal
Recently, a new proposal was introduced that would introduce private variables to classes. It’s rather simple: put a # before the name of a variable, and it becomes private. No extra structural changes needed.
class CarModule { #speed = 0 #milesDriven = 0 accelerate(amount) { // It's virtually impossible for this data to be // accidentally accessed. By no means is it private, // but it's well out of the way of anyone who would // be implementing this module. this.#speed += amount; this.#milesDriven += speed; } getMilesDriven() { return this.#milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); //=> undefined -- we would need to access the internal keys to access the variable.
The private class field proposal is not standard and cannot be done without using Babel as of this writing, so you’ll have to wait a bit for it to be usable on major browsers, Node, etc.
Conclusion
That sums up the various ways you can implement private variables in JavaScript. There isn’t a single “correct” way to do it. These will work for different needs, existing codebases, and other constraints. While each has advantages and disadvantages, ultimately, all methods are equally valid as long as they effectively solve your problem.
Thanks for reading! I hope this provides some insight into how scope and variable privacy can be applied to improve your JavaScript code. This is a powerful technique and can support so many different methods and make your code more usable and bug-free. Try out some new examples for yourself and get a better feel.