Tag: Variables

The Dilemma of Naming Font Size Variables

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:

:root {   /* Font size variables */   --small: 12px;   --medium: 16px;   --large: 24px; }

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.

But imagine having a set of font sizes like this:

$ small: 12px; $ form-label: 14px; $ medium: 16px; $ large: 24px;

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.

h1 {   font-size: clamp(var(--text-size-large), calc(var(--text-size-base) * var(--text-size-scaler)), var(--text-size-huge)); }

We need a better system

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.

International system of units

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.

Traditional point-size names

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:

$ font-14: 14px; $ font-16: 16px; $ font-24: 24px;

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!


The post The Dilemma of Naming Font Size Variables appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,

Hack the “Deploy to Netlify” Button Using Environment Variables to Make a Customizable Site Generator

If you’re anything like me, you like being lazy shortcuts. The “Deploy to Netlify” button allows me to take this lovely feature of my personality and be productive with it.

Deploy to Netlify

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?

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:

// ...   return (     <div>       <img alt={vars.name} src={vars.profilePic} />       <p>{vars.name}</p>       {vars.links.map((l, index) => {         return <Link key={`link$ {index}`} name={l[0]} href={l[1]} />       })}     </div>   )  // ...

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:

<template>   <div>     <p>       <span>Your name:</span>       <input type="text" v-model="name" /> 	</p>     <p>       <span>Your profile picture:</span>	       <input type="text" v-model="propic" />     </p>   </div>    <List v-model:list="list" />    <GenerateButton :name="name" :propic="propic" :list="list" /> </template>

In that List component, we’ll have dual inputs that gather all of the links our users might want to add:

<template>   <div class="list">     Add a link: <br />     <input type="text" v-model="newItem.name" />     <input type="text" v-model="newItem.url" @keyup.enter="addItem" />     <button @click="addItem">+</button>      <ListItem       v-for="(item, index) in list"       :key="index"       :item="item"       @delete="removeItem(index)"     />   </div> </template>

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.


The post Hack the “Deploy to Netlify” Button Using Environment Variables to Make a Customizable Site Generator appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , , , , ,
[Top]

Platform News: Rounded Outlines, GPU-Accelerated SVG Animations, How CSS Variables Are Resolved

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.

Three sets of round yellow buttons, comparing how Chrome, Firefox, and Safari handle outlines.

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.

Side by side comparison of the Performance tab in Chrome DevTools.
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:

html {   --unit-scale: 1.428; }  .box {   /* 5 real-world centimeters */   width: calc(5cm * var(--unit-scale, 1)); }

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.

Screenshot of the crossword game with an open screen reader dialog announcing what is on the screen.
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.

Two CSS rulesets, one as Option A and the other as Option B, both showing how variables are inherited and resolved between elements.

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 post Platform News: Rounded Outlines, GPU-Accelerated SVG Animations, How CSS Variables Are Resolved appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , , ,
[Top]

Want to Write a Hover Effect With Inline CSS? Use CSS Variables.

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:

article {   background: lightgray !important; } article:hover {   /* Doesn't work! */   background: inherit; }

I can override the inline style by using !important, but there’s no way to undo that on hover.

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:

<article style="--custom_color: {{post.custom_color}}">   <h1>{{post.title}}</h1>   {{content}} </article>

Then, that variable can be used to define the hover style in regular CSS:

article {   background: lightgray; } article:hover {   /* Works! */   background: var(--custom_color); }

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.


The post Want to Write a Hover Effect With Inline CSS? Use CSS Variables. appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

Use CSS Variables instead of React Context

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.

Direct Link to ArticlePermalink


The post Use CSS Variables instead of React Context appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Give your Eleventy Site Superpowers with Environment Variables

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:

<?php   define( 'DB_NAME', 'my_cool_db' ); define( 'DB_USER', 'root' ); define( 'DB_PASSWORD', 'root' ); define( 'DB_HOST', 'localhost' );

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:

<?php   $ dotenv = DotenvDotenv::createImmutable(__DIR__); $ dotenv->load();  define( 'DB_NAME', $ _ENV['DB_NAME'] ); define( 'DB_USER', $ _ENV['DB_USER'] ); define( 'DB_PASSWORD', $ _ENV['DB_PASSWORD'] ); define( 'DB_HOST', $ _ENV['DB_HOST'] );

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.

Example of a dot env file showing variables for a node environment, port, API key and API URL.

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:

A default experience of standard HTML content running in localhost with basic styling

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:

The same page as before with the addition of content which is using environment variables

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:

imageBase: process.env.IMAGE_BASE || '/images/', imageProps: process.env.IMAGE_PROPS,

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:

IMAGE_BASE=https://assets.codepen.io/174183/ IMAGE_PROPS=?width=1275&height=805&format=auto&quality=70

If you stop Eleventy (Ctrl+C in terminal) and then run npm start again, then view source in your browser, the rendered image should look like this:

<img src="https://assets.codepen.io/174183/mountains.jpg?width=1275&height=805&format=auto&quality=70" alt="Some lush mountains at sunset" />

This means we can leverage the CodePen asset optimizations only when we need them.

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:

The same page, with the mountain image and now some added private content

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:

A paywall that encourages the person to buy the content while blocking 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:

{% if env.funMode %}   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" />   <style>     body {       font-family: 'Comic Sans MS', cursive;       background: #fc427b;       color: #391129;     }     h1,     .fun {       font-family: 'Lobster';     }     .fun {       font-size: 2rem;       max-width: 40rem;       margin: 0 auto 3rem auto;       background: #feb7cd;       border: 2px dotted #fea47f;       padding: 2rem;       text-align: center;     }   </style> {% endif %}

This snippet is looking to see if our funMode environment variable is true and if it is, it’s adding some “fun” CSS.

Still in base.njk, just before the opening <article> tag, add the following code:

{% if env.funMode %}   <div class="fun">     <p>🎉 <strong>Fun mode enabled!</strong> 🎉</p>   </div> {% endif %}

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:

The main site we’re working on but now, it’s bright pink with Lobster and Comic Sans fonts

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:

  1. Standard site
  2. Private content visible
  3. Fun mode

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 post Give your Eleventy Site Superpowers with Environment Variables appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

The Cicada Principle, revisited with CSS variables

Lea Verou digging up the CSS trickery classic and applying it to clip the backgrounds of some code blocks:

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.

Lea’s post has more trickery using the same concepts.

Direct Link to ArticlePermalink


The post The Cicada Principle, revisited with CSS variables appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Global and Component Style Settings with CSS Variables

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.

Direct Link to ArticlePermalink

The post Global and Component Style Settings with CSS Variables appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Implementing Private Variables In JavaScript

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

The Module Design Pattern

Google relies on the Oxford Dictionary to define a “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.

Let’s take a look at an example class.

class CarModule {   /*     milesDriven = 0;     speed = 0;   */   constructor() {     this.milesDriven = 0;     this.speed = 0;   }   accelerate(amount) {     this.speed += amount;     this.milesDriven += this.speed;   }   getMilesDriven() {     return this.milesDriven;   } }  const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());

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.

The post Implementing Private Variables In JavaScript appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Responsive Designs and CSS Custom Properties: Defining Variables and Breakpoints

CSS custom properties (a.k.a. CSS variables) are becoming more and more popular. They finally reached decent browser support and are slowly making their way into various production environments. The popularity of custom properties shouldn’t come as a surprise, because they can be really helpful in numerous use cases, including managing color palettes, customizing components, and theming. But CSS variables can also be really helpful when it comes to responsive design.

Article Series:

  1. Defining Variables and Breakpoints (This Post)
  2. Building a Flexible Grid System (Coming Tomorrow!)

Let’s consider an <article> element with a heading and a paragraph inside:

<article class="post"> 	<h2 class="heading">Post's heading</h2> 	<p class="paragraph"> 		Lorem ipsum dolor sit amet, consectetur adipisicing elit. 		Laudantium numquam adipisci recusandae officiis dolore tenetur, 		nisi, beatae praesentium, soluta ullam suscipit quas? 	</p> </article>

It’s a common scenario in such a case to change some sizes and dimensions depending on the viewport’s width. One way to accomplish this is by using media queries:

.post { 	padding: 0.5rem 1rem; 	margin: 0.5rem auto 1rem; }  .heading { 	font-size: 2rem; }  @media (min-width: 576px) { 	.post { 		padding: 1rem 2rem; 		margin: 1rem auto 2rem; 	} 	 	.heading { 		font-size: 3rem; 	} }

See the Pen
#1 Building responsive features with CSS custom properties
by Mikołaj (@mikolajdobrucki)
on CodePen.

Such an approach gives us an easy way to control CSS properties on different screen sizes. However, it may be hard to maintain as the complexity of a project grows. When using media queries, keeping code readable and DRY at the same time quite often turns out to be challenging.

The most common challenges when scaling this pattern include:

  • Repeated selectors: Apart from bloating code with multiple declarations, it also makes future refactoring more difficult, e.g. every time a class name changes it requires remembering to update it in multiple places.
  • Repeated properties: Notice that when overwriting CSS rules within media queries, it requires repeating the entire declaration (e.g. font-size: 3rem;) even though it’s just the value (3rem) that actually changes.
  • Repeated media queries: To keep responsive styles contextual, it’s a common practice to include the same media queries in multiple places, close to the styles they override. Unfortunately, it not only makes code heavier, but also might make breakpoints much harder to maintain. On the other hand, keeping all responsive styles in one place, away from their original declarations, may be very confusing: we end up with multiple references to the same elements sitting in completely different places.

We can argue that repeated declarations and queries shouldn’t be such a big deal with proper file compression enabled, at least as long as we’re referring to performance. We can also merge multiple queries and optimize your code with post-processing tools. But wouldn’t it be easier to avoid these issues altogether?

There’s a lot of ways to avoid the issues listed above. One of them, that we will explore in this article, is to use CSS custom properties.

Using CSS variables for property values

There are plenty of amazing articles on the web explaining the concept of CSS custom properties. If you haven’t got chance to get familiar with them yet, I would recommend starting with one of the beginner articles on this topic such as this awesome piece by Serg Hospodarets as we are not going to get into details of the basic usage in this article.

The most common way of utilizing CSS custom properties in responsive design is to use variables to store values that change inside of media queries. To accomplish this, declare a variable that holds a value that is supposed to change, and then reassign it inside of a media query:

:root {   --responsive-padding: 1rem; }  @media (min-width: 576px) {                                :root {     --responsive-padding: 2rem;   } }  .foo { 	padding: var(--responsive-padding); }

Assigning variables to the :root selector is not always a good idea. Same as in JavaScript, having many global variables is considered a bad practice. In real life, try to declare the custom properties in the scope they will actually be used.

This way, we are avoiding multiple rules of the .foo class. We are also separating the logic (changing values) from the actual designs (CSS declarations). Adapting this approach in our example from above gives us the following CSS:

.post { 	--post-vertical-padding: 0.5rem; 	--post-horizontal-padding: 1rem; 	--post-top-margin: 0.5rem; 	--post-bottom-margin: 1rem; 	--heading-font-size: 2rem; }  @media (min-width: 576px) { 	.post { 		--post-vertical-padding: 1rem; 		--post-horizontal-padding: 2rem; 		--post-top-margin: 1rem; 		--post-bottom-margin: 2rem; 		--heading-font-size: 3rem; 	} }  .post { 	padding: var(--post-vertical-padding) var(--post-horizontal-padding); 	margin: var(--post-top-margin) auto  var(--post-bottom-margin); }  .heading { 	font-size: var(--heading-font-size); }

See the Pen
#2 Building responsive features with CSS custom properties
by Mikołaj (@mikolajdobrucki)
on CodePen.

Notice that the use of variables in shorthand properties (e.g. padding, margin or font) allow some very interesting repercussions. As custom properties may hold almost any value (more on this later), even an empty string, it’s unclear how the value of a shorthand property will be separated out into longhand properties that are used in the cascade later. For example, the auto used in the margin property above may turn out to be a top-and-bottom margin, a left-and-right margin, a top margin, a right margin, a bottom margin or a left margin — it all depends on the values of the custom properties around.

It’s questionable whether the code looks cleaner than the one from the previous example, but on a larger scale, it’s definitely more maintainable. Let’s try to simplify this code a bit now.

Notice that some values are repeated here. What if we try to merge duplicate variables together? Let’s consider the following alteration:

:root { 	--small-spacing: 0.5rem; 	--large-spacing: 1rem; 	--large-font-size: 2rem; }  @media (min-width: 576px) { 	:root { 		--small-spacing: 1rem; 		--large-spacing: 2rem; 		--large-font-size: 3rem; 	} }  .post { 	padding: var(--small-spacing) var(--large-spacing); 	margin: var(--small-spacing) auto  var(--large-spacing); }  .heading { 	font-size: var(--large-font-size); }

See the Pen
#3 Building responsive features with CSS custom properties
by Mikołaj (@mikolajdobrucki)
on CodePen.

It looks cleaner but is it actually better? Not necessarily. For the sake of flexibility and readability, this may not be the right solution in every case. We definitely shouldn’t merge some variables just because they accidentally turned out to hold the same values. Sometimes, as long as we’re doing this as a part of a well thought out system, it may help us simplify things and preserve consistency across the project. However, in other cases, such a manner may quickly prove to be confusing and problematic. Now, let’s take a look at yet another way we can approach this code.

Using CSS variables as multipliers

CSS custom properties are a fairly new feature to the modern web. One of the other awesome features that rolled out in the last years is the calc() function. It lets us perform real math operations in live CSS. In terms of the browser support, it’s supported in all browsers that support CSS custom properties.

calc() tends to play very nicely with CSS variables, making them even more powerful. This means we can both use calc() inside custom properties and custom properties inside calc()!

For example, the following CSS is perfectly valid:

:root { 	--size: 2; } 	 .foo { 	--padding: calc(var(--size) * 1rem); /* 2 × 1rem = 2rem */ 	padding: calc(var(--padding) * 2);   /* 2rem × 2 = 4rem */ }

Why does this matter to us and our responsive designs? It means that we can use a calc() function to alter CSS custom properties inside media queries. Let’s say we have a padding that should have a value of 5px on mobile and 10px on desktop. Instead of declaring this property two times, we can assign a variable to it and multiply it by two on larger screens:

:root { 	--padding: 1rem; 	--foo-padding: var(--padding); }  @media (min-width: 576px) {                              	:root { 		--foo-padding: calc(var(--padding) * 2); 	} }  .foo { 	padding: var(--foo-padding); }

Looks fine, however all the values (--padding, calc(--padding * 2)) are away from their declaration (padding). The syntax may also be pretty confusing with two different padding variables (--padding and --foo-padding) and an unclear relationship between them.

To make things a bit clearer, let’s try to code it the other way around:

:root { 	--multiplier: 1; }  @media (min-width: 576px) {                              	:root { 		--multiplier: 2; 	} }  .foo { 	padding: calc(1rem * var(--multiplier)); }

This way, we accomplished the same computed output with much cleaner code! So, instead of using a variable for an initial value of the property (1rem), a variable was used to store a multiplier (1 on small screens and 2 on larger screens). It also allows us to use the --multiplier variable in other declarations. Let’s apply this technique to paddings and margins in our previous snippet:

:root { 	--multiplier: 1; }  @media (min-width: 576px) { 	:root { 		--multiplier: 2; 	} }  .post { 	padding: calc(.5rem * var(--multiplier)) 						calc(1rem  * var(--multiplier)); 	margin:  calc(.5rem * var(--multiplier)) 						auto 						calc(1rem  * var(--multiplier)); }

Now, let’s try to implement the same approach with typography. First, we’ll add another heading to our designs:

<h1 class="heading-large">My Blog</h1> <article class="post"> 	<h2 class="heading-medium">Post's heading</h2> 	<p class="paragraph"> 		Lorem ipsum dolor sit amet, consectetur adipisicing elit. 		Laudantium numquam adipisci recusandae officiis dolore tenetur, 		nisi, beatae praesentium, soluta ullam suscipit quas? 	</p> </article>

With multiple text styles in place, we can use a variable to control their sizes too:

:root { 	--headings-multiplier: 1; }  @media (min-width: 576px) { 	:root { 		--headings-multiplier: 3 / 2; 	} }  .heading-medium { 	font-size: calc(2rem * var(--headings-multiplier)) }  .heading-large { 	font-size: calc(3rem * var(--headings-multiplier)) }

You may have noticed that 3 / 2 is not a valid CSS value at all. Why does it not cause an error then? The reason is that the syntax for CSS variables is extremely forgiving, which means almost anything can be assigned to a variable, even if it’s not a valid CSS value for any existing CSS property. Declared CSS custom properties are left almost entirely un-evaluated until they are computed by a user agent in certain declarations. So, once a variable is used in a value of some property, this value will turn valid or invalid at the computed-value time.

Oh, and another note about that last note: in case you’re wondering, I used a value of 3 / 2 simply to make a point. In real life, it would make more sense to write 1.5 instead to make the code more readable.

Now, let’s take a look at the finished live example combining everything that we discussed above:

See the Pen
#4 Building responsive features with CSS custom properties
by Mikołaj (@mikolajdobrucki)
on CodePen.

Again, I would never advocate for combining calc() with custom properties to make the code more concise as a general rule. But I can definitely imagine scenarios in which it helps to keep code more organized and maintainable. This approach also allows the weight of CSS to be significantly reduced, when it’s used wisely.

In terms of readability, we can consider it more readable once the underlying rule is understood. It helps to explain the logic and relations between values. On the other hand, some may see it as less readable, because it’s tough to instantly read what a property holds as a value without first doing the math. Also, using too many variables and calc() functions at once may unnecessarily obscure code and make it harder to understand, especially for juniors and front-end developers who are not focused on CSS.

Conclusion

Summing up, there’s a lot of ways to use CSS custom properties in responsive design, definitely not limited to the examples shown above. CSS variables can be used simply to separate the values from the designs. They can also be taken a step further and be combined with some math. None of the presented approaches is better nor worse than the others. The sensibility of using them depends on the case and context.

Now that you know how CSS custom properties can be used in responsive design, I hope you will find a way to introduce them in your own workflow. Next up, we’re going to look at approaches for using them in reusable components and modules, so stay tuned for the next post tomorrow!

The post Responsive Designs and CSS Custom Properties: Defining Variables and Breakpoints appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]