Tag: Line

What the Heck is the Command Line?

Now that we know what npm stands for and have a super general idea of what it does and how it fits into web development, we ought to spend a little bit of time looking at the command line, because it’s how we interact with npm.

Guide chapters

  1. Who the Heck is This Guide For?
  2. What the Heck Does “npm” Mean?
  3. What the Heck is the Command Line? (You are here!)
  4. What the Heck is Node?
  5. What the Heck is a Package Manager?
  6. How the Heck Do You Install npm?
  7. How the Heck Do You Install npm Packages?
  8. What the Heck Are npm Commands?
  9. How the Heck Do You Install an Existing npm Project?

An overview of the command line

The command line is a place we can type (predictably enough) commands to be executed directly by our computer. It’s extremely fast, and allows greater admin permissions than most apps that attempt to handle the command line for you. Need to install something on your system, or perhaps update it? The command line can do that, not to mention uninstall them, too. Heck, even server-side languages can run on the command line, opening up a wide range of tools and development techniques.

It’s for those reasons the command line is considered an indispensable tool for many developers. Even if you’re not a developer, odds are you’ve encountered the command line at some point. Maybe you’ve accessed it when you opened the Terminal app in MacOS. Maybe you’ve used one built directly into your code editor—VS Code and many other code editors come with integrated terminal. Perhaps you’ve even come across third-party terminal apps like iTerm or Hyper.

A collage of screenshots showing a terminal and the command line in different applications.

If you’ve used the terminal, it’s possible that much of your command line usage to this point has simply been typing (or pasting) commands somebody else gave you. That’s fine; that’s often how we get things done.

But let’s take a moment to understand exactly what the command line is, and why it’s so broadly used.

Command line vs. terminal

The “command line” and the “terminal” are technically two different and distinct things, but are often used interchangeably. You may also hear the command line called a “shell” or see it abbreviated as “CLI” which is short for “command line interface.”

Pedantic distinctions aside, the terms are often used to mean pretty much the same thing. So just to keep things as simple as possible, I’ll be using them interchangeably from here on out.

Opening the command line

Whatever you want to call it, you probably know the command line as that scary-looking window, perhaps with a dark background and white (sometimes greenish) text, where you type commands that your computer seems to understand, even if you do not.

Screenshot of a blank open terminal window with a dark background.
By default, a terminal window probably looks something like this, though its appearance can vary.

Depending on where and how you’re working on the command line, one of the first things you might notice is an inconspicuous dollar sign character, $ , that’s displayed on the first line where you’re able to start typing. You may have even seen it in other articles or documentation.

This screenshot of Vue’s documentation for using npm includes the $  character in a command example.

It’s common convention to prefix commands with a $ character—but it’s a confusing convention for sure. That’s because there’s no need to type it. It’s literally not part of the command. Instead, $ signifies a command that’s meant to be run in a terminal.

So here’s the first rule to know about working with the command line: if you find yourself typing or copying an instruction that includes the $ character, know that there is no need to include it in your work; the terminal’s got it covered.

## No need to copy the $  $  npm run build

You may see something else beginning a line instead of $ , like >, _ , or even an arrow. Again, whatever it is, it’s almost certainly not meant to be typed or pasted directly into the command line. Whether or not documentation or other tutorials should include the starting character of a line is an entirely different conversation (one that Chris has discussed at length). Either way, it has the potential to be confusing, so I want to make sure that’s called out.

What does the command line do?

Movies and TV shows often portray a terminal as something fast-typing hackers use in a dark, secluded room. That’s partly just because it makes for good entertainment for folks who probably wouldn’t know a real terminal from the cascading letters in The Matrix. (Nor should they; I couldn’t tell if a surgery portrayed on a TV show is accurate, and am perfectly content leaving that distinction to the professionals.)

But the command line isn’t exactly for writing code. As the name “command line” implies, it’s for writing commands. I suppose you could say that all coding is commands to some extent, so this is admittedly a bit of a blurry line. But generally speaking, code in a terminal is written differently than it is in a code editor. Instead, you use the terminal to boss your computer around with commands you want it to run immediately.

The advantages of the command line

You may wonder why developers like to work in the command line in the first place. You might prefer a nice app, or graphical user interface (GUI for short, sometimes pronounced “gooey”) where you can see all your options and find the best one visually. That’s perfectly fine, and we’ll talk a bit about GUIs in this chapter and provide examples of them.

Many developers feel that way, even if it doesn’t seem like it. But working within the command line brings some advantages that aren’t always easy to replicate in a visual app.

It grants you god-like system privileges

The command line is what computer science-y people refer to as a “privileged environment.” That might sound like it refers to a Yale frat house, but it just means it’s a place where there are very few restrictions on what you are allowed to do; a place without guardrails.

This is where the command line’s daunting reputation comes from—whatever command you type, as far as it’s valid, is executed immediately and, often, irreversibly. It’s capable of interacting with the hidden files your operating system tries to prevent you from editing. You have the power to access anything in the system. You even have the power to interact with similar core files on a remote server—and we all know the adage that great responsibility comes with that kind of power.

It might be helpful to think of the command line like a lazy security guard. It assumes you always know what you’re doing and lets you right through the entrance. That makes it a bit risky, yes, but it also makes it very powerful, and the perfect choice for certain tasks and projects.

It’s super fast

Another advantage the command line has over typical apps is: it’s fast.

This isn’t always the case; the command line’s speed tends to be overstated and depends largely on the task in question. But when it is faster, it can often be many times faster. Plus, the place where the command line really shines tends to be exactly the place where code projects need speed the most, which is downloading and creating files.

As we’ll see in other chapters of this guide, a key part of what npm does is install things on your machine (usually in a folder that’s designated for the project you’re working on). That’s what makes the command line great for working with a package manager (we’ll get to what that means, too) like npm—it downloads and transmits files between computers—generally much, much faster than, say, using a browser to do it.

The command line enables npm to generate tons of files with incredible speed. The ability to run a single command that installs, updates, or deletes those files together in one fell swoop at high speed makes the terminal the fastest, most efficient tool for many jobs.

It does what other languages cannot

One more other reason working in the terminal is so advantageous is that it’s the place where many of the tools you might want to use in your project are already available to you without any additional setup.

But let’s back up a bit.

When you hear the phrase “server-side language,” you might think of PHP, Ruby, or Java. Maybe it’s even newer entries into the space, like Rust or Go. You might even already know that Node belongs on that list, but if not, forgive me for jumping ahead just a bit.

Anyway, when most people think of server-side languages like these, they tend to think of a web server waiting for requests and then responding to them. WordPress, for example, sits idle until it receives a request that fires up PHP. When you send a new tweet, that’s a request on Twitter’s servers that eventually hits a Ruby method in Rails.

Server-side languages are rightly considered more powerful, so to speak, than web languages. HTML, CSS, and JavaScript are wonderful, but they can’t work with a file system, send email, process images, issue system commands, interact with the operating system, or run scheduled tasks; among many, many other things an app or website might need to do. By default, JavaScript in the browser can’t even run unless somebody is actively looking at the web page in their browser.

It’s normal to think of server-side languages as the powerful engine behind more robust apps and software. And, in many cases, that’s accurate. But let’s take a moment to recognize that for the purpose of running code, your machine is a server. Not a web server although (it could be one, but that would be weird and probably unwise). But a server, nonetheless.

A photo of a laptop that is half-closed with a sheet of paper from a yellow legal pad that says This 9s a server (yes, really), do not close the lid!
You probably don’t think of your machine as a server—nor should you. But it can run server-side programming languages.

You can install and run any of the server-side languages we’ve mentioned, and perhaps you’ve already even done so at some point (or at least tried to). You might have installed PHP so you can run WordPress (though these days there are much nicer ways to do it), or you may have installed Ruby so that you could follow some tutorials about Rails, just as examples.

Or, maybe not. Maybe you’ve never installed a whole dang programming language before. Either way, just know that these languages run on a server rather than a web browser—and for that purpose, your machine is a server.

Beyond that, many of the tools you might want to use with your development workflow—things like Sass for compiling CSS—actually run on server-side languages. So using the command line puts you in the place where all the most powerful tools are readily available.

Using an app instead of the command line

We briefly touched on GUIs earlier in this article. It’s worth noting that some command line tasks have corresponding GUIs that make working with the command line more visual and programmatic.

Good examples include GitHub Desktop (for managing code repositories) and CodeKit (for processing, bundling and compiling assets), though the Source Control tab in VS Code also qualifies. Even though GUIs like these are generally focused on specific duties, they let you get things done through a nice visual user interface, in an actual app window that’s outside of the terminal window.

Screenshot of the CodeKit app after running ESlint showing how an app can visualize the command line.
CodeKit can execute a command to check code formatting with tools like ESLint without directly using the command line.

A GUI is lovely to have as an option, and even though I’ve become quite comfortable working on the command line over the years, I still wish there were more GUIs to do the things the command line makes possible—both for my own convenience and to lower the barrier to entry for new developers.

I believe the reason there aren’t more such apps, however, is because of speed. It’s much quicker and easier to build a command line interface (CLI) than it is to put together a full-fledged app, often by orders of magnitude. So if we want nice things today, the command line is often the place we need to go for them.

What’s next

We just spent some time getting acquainted with the command line. Even though the command line is not specific to npm, it’s core to working with npm. It’s the interface from which we tell the system what to do, granting us incredible powers at the system or server level to complete tasks in large swaths at breakneck speeds. As a package manager, npm is in the business of installing, updating, and deleting files (among other things) for a web project. The command line is how we communicate with npm to do all of that.

Next up, we’re going to break down what npm is a little more by focusing on the first letter in the abbreviation: “n” for Node. What the heck is that and why is it in the name? That’s where we turn our focus next.


What the Heck is the Command Line? originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, ,

Line length revisited: following the research

Mary Dyson produces nitty gritty research on the long-accepted notion that shorter line lengths are more legible than longer ones. The study finds that shorter lines do not necessarily lead to faster reading. If you’re looking for a definitive answer to use in your next design review debate, though, no dice. The big finding is that long lines don’t slow things down as much as previously thought, not that they’re better or worse.

But there’s so much more meat in here that I found much more interesting, mostly because I’m largely ignorant on the topic and gained a dollop of context on writing, legibility, and behavior.

Things like…

There’s a term for transitioning between lines of text

It’s return sweeps. You know, like your eye hits the Return key at the end of the line and sweeps to the start of the next line. Then, there are undershoots. The idea is that eyes may not sweep to the exact start of the next line, instead stopping a bit short.

Showing four muted lines of test with jump arrows across each line taking the eye to the end of the line, followed by dashed arrow leading to the next line. Red arrows highlight where the eye could undershoot a new line and miss content.

Those little rapid eye movements between words and phrases? Those are called saccades. I had to look that up.

The impact of undershoots is what’s being challenged

The previous research we’ve relied on for years comes from 1940(!), a time when we obviously were more concerned with paper pages than bright digital displays. Longer lines, it said, increased the likelihood that eyes undershoot during a return sweep, and undershooting results in a regression that results in a 130ms to 250ms delay where the brain needs to get its bearings. The report refers to that as undersweep-fixation.

We can still process words during undersweep-fixation

This report cites a 2019 study that tried to correct undershoots by bolding the first word at the start of each new line, sort of like an anchor that naturally draws the eye closer to the left margin.

The 2019 study did find that the bolded words did decrease undershot return sweeps But despite that, reading speed did not improve. That’s the impetus for challenging the long-held assumption that shorter is better.

Mary explains further:

In seeking to reconcile why longer line lengths may not slow down reading on screen but do so when reading print, I outlined some differences, e.g. visual angle, time spent scrolling. But although physical differences between reading from screen and reading print still exist, we now have direct evidence to explain why longer lines were read at least as fast as shorter lines. Readers can process words during the brief fixation when undershooting the start of the new line. This saves time in subsequent processing. Now we might also acknowledge that there is greater consistency between the range of optimal line lengths for print and screen.

Where does this leave us today?

Well, nowhere closer to a clear answer we can use in our day-to-day work. But it’s good to dust off our collection of design and copywriting best practices and know that line length is less of a constraint than perhaps it has been.

Again, none of this tells us whether long or short lines are better. Mary ends her report by saying she cannot definitely recommend using longer lines of text because there are clear because there are still some factors at play, including:

  • Shorter lines are more effective for people with dyslexia.
  • More questions about return sweeps and undershooting need to be answered.
  • Several other studies indicate that users prefer shorter lines and judge longer lines as more difficult to read.

To Shared LinkPermalink on CSS-Tricks


The post Line length revisited: following the research appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Static vs. Dynamic vs. Jamstack: Where’s The Line?

You’ll often hear developers talking about “static” vs. “dynamic” sites, or you may have heard someone use the term Jamstack. What do these terms mean, and when does a “static” site become either a Jamstack or dynamic site? These questions sound simple, but they’re more nuanced than they appear. Let’s explore these terms to gain a deeper understanding of Jamstack.

Finding the line

What’s the difference between a chair and a stool? Most people will respond that a chair has four legs and back support, whereas a stool has three legs with no back support.

Two brown backed leather chairs with a black frame and legs around a white table with a decorative green succulent plant in a white vase.
Credit: Rumman Amin

Two tall brown barstools with three legs and a brass frame under a natural wood countertop with a decorative green houseplant.
Credit: Rumman Amin

OK, that’s a great starting point, but what about these?

The more stool-like a chair becomes, the fewer people will unequivocally agree that it’s a chair. Eventually, we’ll reach a point where most people agree it’s a stool rather than a chair. It may sound like a silly exercise, but if we want to have a deep appreciation of what it means to be a chair, it’s a valuable one. We find out where the limits of a chair are for most people. We also build an understanding of the gray area beyond. Eventually, we get to the point where even the biggest die-hard chair fans concede and admit there’s a stool in front of them.

As interesting as chairs are, this is an article about website delivery technology. Let’s perform this same exercise for static, dynamic, and Jamstack websites.

At a high level

When you go to a website in your browser, there’s a lot going on behind the scenes:

  1. Your browser performs a DNS lookup to turn the domain name into an IP address.
  2. It requests an HTML file from that IP address.
  3. The webserver sends back the requested file.
  4. As the browser renders the web page, it may come across a reference for an asset, such as a CSS, JavaScript, or image file. The browser then performs a request for this asset.
  5. This cycle continues until the browser has all the files for the web page. It’s not unusual for a single webpage to make 50+ requests.

For every request, the response from the webserver is always a static file, even on a dynamic website. You could save these files to a USB drive, email them to a friend just like any other file on your computer.

When comparing static and dynamic, we’re talking about what the webserver is doing. On a static site, the files the browser requests already exist on the webserver. The webserver sends them back exactly as they are. On a dynamic site, the response gets generated by software. This software might connect to a database to retrieve data, build a layout from template files, and add today’s date to the footer. It does all of this for every request.

That’s the foundational difference between static and dynamic websites.

Where does Jamstack fit in?

Static websites are restrictive. They’re great for informational websites; however, you can’t have any dynamic content or behavior by definition. Jamstack blurs the line between static and dynamic. The idea is to take advantage of all the things that make static websites awesome while enabling dynamic functionality where necessary.

The ‘stack’ in Jamstack is a misnomer. The truth is, Jamstack is not a stack at all. It’s a philosophy that exhibits a striking resemblance to The 5 Pillars of the AWS Well-Architected Framework. The ambiguity in the term has led to extensive community discussion about what it means to be Jamstack.

What is Jamstack?

Jamstack is a superset of static. But to truly understand Jamstack, let’s start with the seeds that led to the coining of the term.

In 2002, the late Aaron Swartz published a blog post titled “Bake, Don’t Fry.” While Aaron didn’t coin “Bake, Don’t Fry,” it’s the first time I can find someone recognizing the benefits of static websites while breaking out perceived constraints of the word.

I care about not having to maintain cranky AOLserver, Postgres and Oracle installs. I care about being able to back things up with scp. I care about not having to do any installation or configuration to move my site to a new server. I care about being platform and server independent.

If we trawl through history, we can find similar frustrations that led to Jamstack seeds:

  • Ben and Mena Trott created MovableType because of a [d]issatisfaction with existing blog CMSes — performance, stability.
  • Tom Preston-Werner created Jekyll to move away from complexity:

    I already knew a lot about what I didn’t want. I was tired of complicated blogging engines like WordPress and Mephisto. I wanted to write great posts, not style a zillion template pages, moderate comments all day long, and constantly lag behind the latest software release.

  • Steve Francia created Hugo for performance:

    The past few years this blog has [been] powered by wordpress [sic] and drupal prior to that. Both are are fine pieces of software, but over time I became increasingly disappointed with how they are both optimized for writing content even though significantly most common usage is reading content. Due to the need to load the PHP interpreter on each request it could never be considered fast and consumed a lot of memory on my VPS.

The same themes surface as you look at the origins of many early Jamstack tools:

  • Reduce complexity
  • Improve performance
  • Reduce vendor lock-in
  • Better workflows for developers

In the past 20 years, JavaScript has evolved from a language for adding small interactions to a website to becoming a platform for building rich web applications in the browser. In parallel, we’ve seen a movement of splitting large applications into smaller microservices. These two developments gave rise to a new way of building websites where you could have a static front-end decoupled from a dynamic back-end.

In 2015, Mathias Biilmann wanted to talk about this modern way of building websites but was struggling with the constricting definition of static:

We were in this space of modern static websites. That’s a really bad description of what we’re doing, right? And we kept having that problem that, talking to people about static sites, they would think about something very static. They would think about a brochure or something with no moving parts. A little one-pager or something like that.

To break out of these constraints, he coined the term “Jamstack” to talk about this new approach, and it caught on like wildfire. What was old static technology from the 90s became new again and pushed to new limits. Many developers caught on to the benefits of the Jamstack approach, which helped Jamstack grow into the thriving ecosystem it is today.

Aaron Swartz put it nicely, 13 years before Jamstack was coined: keep a strict separation between input (which needs dynamic code to be processed) and output (which can usually be baked). In other words, decouple the front end from the back end. Prerender content whenever possible. Layer on dynamic functionality where necessary. That’s the crux of Jamstack.

The reason you might want to build a Jamstack site over a dynamic site come down to the six pillars of Jamstack:

Security

Jamstack sites have fewer moving parts and less surface area for malicious exploitation from outside sources.

Scale

Jamstack sites are static where possible. Static sites can live entirely in a CDN, making them much easier and cheaper to scale.

Performance

Serving a web page from a CDN rather than generating it from a centralized server on-demand improves the page load speed.

Maintainability

Static websites are simple. You need a webserver capable of serving files. With a dynamic site, you might need an entire team to keep a website online and fast.

Portability

Again, a static website is made up of files. As long as you find a webserver capable of serving website files, you can move your site anywhere.

Developer experience

Git workflows are a core part of software development today. With many legacy CMSs, it’s difficult to have Git development workflows. With a Jamstack site, everything is a file making it seamless to use Git.

Chris touches on some of these points in a deep-dive comparison between Jamstack and WordPress. He also compares the reasons for choosing a Jamstack architecture versus a server-side one in “Static or Not?”.

Let’s use these pillars to evaluate Jamstack use cases.

Where is the edge of static and Jamstack?

Now that we have the basics of static and Jamstack, let’s dive in and see what lies at the edge of each definition. We have four categories each edge case can fall under.

  • Static – This strictly adheres to the definition of static.
  • Basically static – While not precisely static, most people would call it a static site.
  • Jamstack – A static frontend decoupled from a dynamic backend.
  • Dynamic – Renders web pages on-demand.

Many of these use cases can be placed in multiple categories. In this exercise, we’re putting them in the most restrictive category they fit.

JavaScript interaction Static

Let’s start with an easy one. I have a static site that uses JavaScript to create a slideshow of images.

The HTML page, JavaScript, and images are all static files. All of the HTML manipulation required for the slideshow to function happens in the browser with no external influence.

Cookies Static

I have a static site that adds a banner to the top of the page using JavaScript if a cookie exists. A cookie is just a header. The rest of the files are static.

External assets Basically Static

On a web page, we can load images or JavaScript from an external source. This external source may generate these assets dynamically on request. Would that mean we have a dynamic site?

Most people, including myself, would consider this a static site because it basically is. But if we’re strict to the definition, it doesn’t fit the bill. Having any part of the page generated dynamically defiles the sacred harmony of static.

iFrames Basically Static

An inline frame allows you to embed an HTML page within another HTML page. iFrames are commonly used for embedding Google Maps, Facebook Like buttons, and YouTube videos on a webpage.

Again, most people would still consider this a static site. However, these embeds are almost always from a dynamically-generated source.

Forms Basically Static

A static site can undoubtedly have a form on it. The dilemma comes when you submit it. If you want to do something with the data, you almost certainly need a dynamic back-end. There are plenty of form submission services you can use as the action for your form.

I can see two ways to argue this:

  1. You’re submitting a form to an external website, and it happens to redirect back afterward. This separation means the definition of static remains intact.
  2. This external service is a core workflow on your website, the definition of static no longer works.

In reality, most people would still consider this a static site.

Ajax requests Jamstack

An Ajax request allows a developer to request data from an external source without reloading the page. We’re in the same boat as the above situations of relying on a third party. It’s possible the endpoint for the Ajax call is a static JSON file, but it’s more likely that it’s dynamically-generated.

The nature of how Ajax data is typically used on a website pushes it past a static website into Jamstack territory. It fits well with Jamstack as you can have a site where you prerender everything you can, then use Ajax to layer on any dynamic functionality or content on the site.

Embedded eCommerce Jamstack

There are services that allow you to add eCommerce, even to static websites. Behind the scenes, they’re essentially making Ajax requests to manage items in a shopping cart and collect payment details.

Single page application (SPA) Jamstack

The title alone puts it out of static site contention. A SPA uses Ajax calls to request data. The presentation layer lives entirely in the front end, making it Jamtastic.

Ajax call to a serverless function Jamstack

Whether the endpoint of an Ajax call is serverless with something like AWS Lambda, goes to your Kubernetes clustered Node.js back-end, or a simple PHP back-end, it doesn’t matter. The key for Jamstack is the front end is independent of the back end.

Reverse proxy in front of a webserver Static

Adding a reverse proxy in front of the webserver for a static site must make it dynamic, right? Well, not so fast. While a proxy is software that adds a dynamic element to the network, as long as the file on the server is precisely the file the browser receives, it’s still static.

A webserver, modem, and every piece of network infrastructure in between are running software. If adding a proxy makes a static site dynamic, then nothing is static.

CDN Static

A CDN is a globally-distributed reverse proxy, so it falls into the same category as a reverse proxy. CDNs often add their own headers. This still doesn’t impact the prestigious static status as the headers aren’t part of the file sitting on the server’s hard drive.

CDN in front of a dynamic site with a 200-year cache expiration time Dynamic

OK, 200 years is a long expiry time, I’ll give you that. There are two reasons this is neither a static nor Jamstack site:

  1. The first request isn’t cached, so it generates on demand.
  2. CDNs aren’t designed for persistent storage. If, after one week, you’ve only had five hits on your website, the CDN might purge your web page from the cache. It can always retrieve the web page from the origin server, which would dynamically render the response.

WordPress with a static output Static

Using a WordPress plugin like WP2Static lets you create and manage your website in WordPress and output a static website whenever something changes.

When you do this, the files the browser requests already exist on the webserver, making it a static website—a subtle but important distinction from having a CDN in front of a dynamic site.

Edge computing Dynamic

Many companies are now offering the ability to run dynamic code at the edge of a CDN. It’s a powerful concept because you can have dynamic functionality without adding latency to the user. You can even use edge computation to manipulate HTML before sending it to the client.

It comes down to how you’re using edge functions. You could use an edge function to add a header to particular requests. I would argue this is still a static site. Push much beyond this, where you’re manipulating the HTML, and you’ve crossed the dynamic boundary.

It’s hard to argue it’s a Jamstack site as it doesn’t adhere to some of the fundamental benefits: scale, maintainability, and portability. Now, you have a piece of your core infrastructure that’s changing HTML on every request, and it will only work on that particular hosting infrastructure. That’s getting pretty far away from the blissful simplicity of a static site.

One of the elegant things about Jamstack is the front end and back end are decoupled. The backend is made up of APIs that output data. They don’t know or care how the data is used. The front end is the presentation layer. It knows where to get dynamic data from and how to render it. When you break this separation of concerns, you’ve crossed into a dynamic world.

Dynamic Persistent Rendering (DPR) Dynamic

DPR is a strategy to reduce long build times on large static site generator (SSG) sites. The idea is the SSG builds a subset of the most popular pages. For the rest of the pages, the SSG builds them on-demand the first time they’re requested and saves them to persistent storage. After the initial request, the page behaves precisely like the rest of the built static pages.

Long build times limit large-scale use cases from choosing Jamstack. If all the SSG tooling were GoLang-based, we probably wouldn’t need DPR. However, that’s not the direction most Jamstack tooling has taken, and build performance can be excruciatingly long on big websites.

DPR is a means to an end and a necessity for Jamstack to grow. While it allows you to use Jamstack workflows on massive websites, ironically, I don’t think you can call a site using DPR a Jamstack site. Running software on-demand to generate a web page certainly sounds dynamicy. After the first request, a page served using DPR is a static page which makes DPR “more static” than putting a CDN in front of a dynamic site. However, it’s still a dynamic site as there isn’t a separation between frontend and backend, and it’s not portable, one of the pillars of a Jamstack site.

Incremental Static Regeneration (ISR) Dynamic

ISR is a similar but subtly different strategy to DPR to reduce long build times on large SSG sites. The difference is you can revalidate individual pages periodically to mimic a dynamic site without doing an entire site build.

Requests to a page without a cached version fall back to a stale version of that page or a generic loading page.

Again, it’s an exciting technology that expands what you can do with Jamstack workflows, but dynamically generating a page on-demand sounds like something a dynamic site would do.

Flat file CMS Dynamic

A flat file CMS uses text files for content rather than a database. While flat file CMSs remove a dynamic element from the stack, it’s still dynamically rendering the response.

The lines have been drawn

Exploring and debating these edge cases gives us a better understanding of the limits of all of these terms. The point of this exercise isn’t to be dogmatic about creating static or Jamstack websites. It’s to give us a common language to talk about the tradeoffs you make as you cross the boundary from one concept to another.

There’s absolutely nothing wrong with tradeoffs either. Not everything can be a purely static website. In many cases, the trade-offs make sense. For example, let’s say the front end needs to know the country of the visitor. There are two ways to do this:

  1. On page load, perform an Ajax call to query the country from an API. (Jamstack)
  2. Use an edge function to dynamically insert a country code into the HTML on response. (Dynamic)

If having the country code is a nice-to-have and the web page doesn’t need it immediately, then the first approach is a good option. The page can be static and the API call can fail gracefully if it doesn’t work. However, if the country code is required for the page, dynamically adding it using an edge function might make more sense. It’ll be faster as you don’t need to perform a second request/response cycle.

The key is understanding the problem you’re solving and thinking through the trade-offs you’re making with different approaches. You might end up with the majority of your site Jamstack and a portion dynamic. That’s totally fine and might be necessary for your use case. Typically, the closer you can get to static, the faster, more secure, and more scalable your site will be.

This is only the beginning of the discussion, and I’d love to hear your take. Where would you draw the lines? What do static and Jamstack mean to you? Are you sitting on a chair or stool right now?


The post Static vs. Dynamic vs. Jamstack: Where’s The Line? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Building a Command Line Tool with Nodejs and Fauna

Command line tools are one of the most popular applications we have today. We use command line tools every day, and they range from git, npm or yarn. Command line tools are very fast and useful for automating applications and workflows.

We will be building a command line tool with Node.js and Fauna for our database in this post. In addition, we will be creating a random quotes application using Node.js, and add permission and a keyword for our app.

Prerequisites

To take full advantage of this tutorial, make sure you have the following installed on your local development environment:

  • Node.js version >= 16.x.x installed.
  • Have access to one package manager such as npm or yarn.
  • Access to Fauna dashboard.

Getting Started with Fauna

Register a new account using email credentials or a GitHub account. You can register a new account here. Once you have created a new account or signed in, you are going to be welcomed by the dashboard screen:

Creating a New Fauna Instance

To create a new database instance using Fauna services, you have to follow some simple steps. On the dashboard screen, press the button New Database:

Next, enter the name of the database and save. Once a database instance is set up, you are ready to access the key. Use access keys to connect authorization and a connection to the database from a single-page application. To create your access key, navigate to the side menu, and go to the Security tab and click on the New Key button.

Creating a Collection

Navigate to your dashboard, click on the Collections tab from the side menu, press the New Collection, button, input your desired name for the new collection, and save.

Creating Indexes

To complete setup, create indexes for our application. Indexes are essential because searching documents are done using indexes in Fauna by matching the user input against the tern field. Create an index by navigating to the Indexes tab of our Fauna dashboard.

Now, we are ready to build our notes command-line application using Node.js and our database.

Initializing a Node.js App and Installing Dependencies

This section will initialize a Node.js application and install the dependencies we need using the NPM package. We are also going to build a simple quotes application from this link.

Getting Started

To get started, let’s create a folder for our application inside the project folder using the code block below on our terminal:

mkdir quotes_cli cd quotes_cli touch quotes_app npm init -y

In the code block above, we created a new directory, navigated into the directory, and created a new file called quotes_app, and ended by initializing the npm dependencies. Next, add a package to make requests to the quotes server using axios.

npm i axios

Add a package for coloring our texts, chalk is an NPM package that helps us add color to print on the terminal. To add chalk, use the code block below

npm i chalk Let’s also import a dotenv package using the code block:

npm i dotenv

Building the Quotes App

In our quotes_app file, let’s add the code block below

const axios = require('axios') const chalk = require('chalk'); const dotenv = require('dotenv'); const url = process.env.APP_URL axios({   method: 'get',   url: url,   headers: { 'Accept': 'application/json' }, }).then(res => {   const quote = res.data.contents.quotes[0].quote   const author = res.data.contents.quotes[0].author   const log = chalk.red(`$ {quote} - $ Fortune Ikechi`)    console.log(log) }).catch(err => {   const log = chalk.red(err)    console.log(log) })

In the code block above, we imported axios, chalk, and dotenv. We added the URL of our database, our Fauna database, and using axios, we made a GET request to the URL and added headers to enable us to get our response in json.

To log a quote, we use JavaScript promises to log the quote and its author on our console and added a catch method for catching errors.

Before we run, let’s change the permissions on our file using the code below:

chmod +x quotes_app

Next, run the application using our keyword below:

./quotes_app

We should get a result similar to the image below

Conclusion

In this article, we learned more about Fauna and Node.js command-line tools. You can extend the application to be able to add date reminders in real-time.

Here is a list of some resources that you might like after reading this post:


The post Building a Command Line Tool with Nodejs and Fauna appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Better Line Breaks for Long URLs

CSS-Tricks has covered how to break text that overflows its container before, but not much as much as you might think. Back in 2012, Chris penned “Handling Long Words and URLs (Forcing Breaks, Hyphenation, Ellipsis, etc)” and it is still one of only a few posts on the topic, including his 2018 follow-up Where Lines Break is Complicated. Here’s all the Related CSS and HTML.

Chris’s tried-and-true technique works well when you want to leverage automated word breaks and hyphenation rules that are baked into the browser:

.dont-break-out {   /* These are technically the same, but use both */   overflow-wrap: break-word;   word-wrap: break-word;    word-break: break-word;    /* Adds a hyphen where the word breaks, if supported (No Blink) */   hyphens: auto; }

But what if you can’t? What if your style guide requires you to break URLs in certain places? These classic sledgehammers are too imprecise for that level of control. We need a different way to either tell the browser exactly where to make a break.

Why we need to care about line breaks in URLs

One reason is design. A URL that overflows its container is just plain gross to look at.

Then there’s copywriting standards. The Chicago Manual of Style, for example, specifies when to break URLs in print. Then again, Chicago gives us a pass for electronic documents… sorta:

It is generally unnecessary to specify breaks for URLs in electronic publications formats with reflowable text, and authors should avoid forcing them to break in their manuscripts.

Chicago 17th ed., 14.18

But what if, like Rachel Andrew (2015) encourages us, you’re designing for print, not just screens? Suddenly, “generally unnecessary” becomes “absolutely imperative.” Whether you’re publishing a book, or you want to create a PDF version of a research paper you wrote in HTML, or you’re designing an online CV, or you have a reference list at the end of your blog post, or you simply care how URLs look in your project—you’d want a way to manage line breaks with a greater degree of control.

OK, so we’ve established why considering line breaks in URLs is a thing, and that there are use cases where they’re actually super important. But that leads us to another key question…

Where are line breaks supposed to go, then?

We want URLs to be readable. We also don’t want them to be ugly, at least no uglier than necessary. Continuing with Chicago’s advice, we should break long URLs based on punctuation, to help signal to the reader that the URL continues on the next line. That would include any of the following places:

  • After a colon or a double slash (//)
  • Before a single slash (/), a tilde (~), a period, a comma, a hyphen, an underline (aka an underscore, _), a question mark, a number sign, or a percent symbol
  • Before or after an equals sign or an ampersand (&)

At the same time, we don’t want to inject new punctuation, like when we might reach for hyphens: auto; rules in CSS to break up long words. Soft or “shy” hyphens are great for breaking words, but bad news for URLs. It’s not as big a deal on screens, since soft hyphens don’t interfere with copy-and-paste, for example. But a user could still mistake a soft hyphen as part of the URL—hyphens are often in URLs, after all. So we definitely don’t want hyphens in print that aren’t actually part of the URL. Reading long URLs is already hard enough without breaking words inside them.

We still can break particularly long words and strings within URLs. Just not with hyphens. For the most part, Chicago leaves word breaks inside URLs to discretion. Our primary goal is to break URLs before and after the appropriate punctuation marks.

How do you control line breaks?

Fortunately, there’s an (under-appreciated) HTML element for this express purpose: the <wbr> element, which represents a line break opportunity. It’s a way to tell the browser, Please break the line here if you need to, not just any-old place.

We can take a gnarly URL, like the one Chris first shared in his 2012 post:

http://www.amazon.com/s/ref=sr_nr_i_o?rh=k%3Ashark+vacuum%2Ci%3Agarden&keywords=shark+vacuum&ie=UTF8&qid=1327784979

And sprinkle in some <wbr> tags, “Chicago style”:

http:<wbr>//<wbr>www<wbr>.<wbr>amazon<wbr>.com<wbr>/<wbr>s/<wbr>ref<wbr>=<wbr>sr<wbr>_<wbr>nr<wbr>_<wbr>i<wbr>_o<wbr>?rh<wbr>=<wbr>k<wbr>%3Ashark<wbr>+vacuum<wbr>%2Ci<wbr>%3Agarden<wbr>&<wbr>keywords<wbr>=<wbr>shark+vacuum<wbr>&ie<wbr>=<wbr>UTF8<wbr>&<wbr>qid<wbr>=<wbr>1327784979

Even if you’re the most masochistic typesetter ever born, you’d probably mark up a URL like that exactly zero times before you’d start wondering if there’s a way to automate those line break opportunities.

Yes, yes there is. Cue JavaScript and some aptly placed regular expressions:

/**  * Insert line break opportunities into a URL  */ function formatUrl(url) {   // Split the URL into an array to distinguish double slashes from single slashes   var doubleSlash = url.split('//')    // Format the strings on either side of double slashes separately   var formatted = doubleSlash.map(str =>     // Insert a word break opportunity after a colon     str.replace(/(?<after>:)/giu, '$ 1<wbr>')       // Before a single slash, tilde, period, comma, hyphen, underline, question mark, number sign, or percent symbol       .replace(/(?<before>[/~.,-_?#%])/giu, '<wbr>$ 1')       // Before and after an equals sign or ampersand       .replace(/(?<beforeAndAfter>[=&])/giu, '<wbr>$ 1<wbr>')     // Reconnect the strings with word break opportunities after double slashes     ).join('//<wbr>')    return formatted }

Try it out

Go ahead and open the following demo in a new window, then try resizing the browser to see how the long URLs break.

This does exactly what we want:

  • The URLs break at appropriate spots.
  • There is no additional punctuation that could be confused as part of the URL.
  • The <wbr> tags are auto-generated to relieve us from inserting them manually in the markup.

This JavaScript solution works even better if you’re leveraging a static site generator. That way, you don’t have to run a script on the client just to format URLs. I’ve got a working example on my personal site built with Eleventy.

If you really want to break long words inside URLs too, then I’d recommend inserting those few <wbr> tags by hand. The Chicago Manual of Style has a whole section on word division (7.36–47, login required).

Browser support

The <wbr> element has been seen in the wild since 2001. It was finally standardized with HTML5, so it works in nearly every browser at this point. Strangely enough, <wbr> worked in Internet Explorer (IE) 6 and 7, but was dropped in IE 8, onward. Support has always existed in Edget, so it’s just a matter of dealing with IE or other legacy browsers. Some popular HTML-to-PDF programs, like Prince, also need a boost to handle <wbr>.

One more possible solution

There’s one more trick to optimize line break opportunities. We can use a pseudo-element to insert a zero width space, which is how the <wbr> element is meant to behave in UTF-8 encoded pages anyhow. That’ll at least push support back to IE 9, and perhaps more importantly, work with Prince.

/**   * IE 8–11 and Prince don’t recognize the `wbr` element,  * but a pseudo-element can achieve the same effect with IE 9+ and Prince.  */ wbr:before {   /* Unicode zero width space */   content: "0B";   white-space: normal; }

Striving for print-quality HTML, CSS, and JavaScript is hardly new, but it is undergoing a bit of a renaissance. Even if you don’t design for print or follow Chicago style, it’s still a worthwhile goal to write your HTML and CSS with URLs and line breaks in mind.

References


The post Better Line Breaks for Long URLs appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

Converting and Optimizing Images From the Command Line

Images take up to 50% of the total size of an average web page. And if images are not optimized, users end up downloading extra bytes. And if they’re downloading extra bytes, the site not only takes that much more time to load, but users are using more data, both of which can be resolved, at least in part, by optimizing the images before they are downloaded.

Researchers around the world are busy developing new image formats that possess high visual quality despite being smaller in size compared to other formats like PNG or JPG. Although these new formats are still in development and generally have limited browser support, one of them, WebP, is gaining a lot of attention. And while they aren’t really in the same class as raster images, SVGs are another format many of us have been using in recent years because of their inherently light weight.

There are tons of ways we can make smaller and optimized images. In this tutorial, we will write bash scripts that create and optimize images in different image formats, targeting the most common formats, including JPG, PNG, WebP, and SVG. The idea is to optimize images before we serve them so that users get the most visually awesome experience without all the byte bloat.

Our targeted directory of images

Our directory of optimized images

This GitHub repo has all the images we’re using and you’re welcome to grab them and follow along.

Set up

Before we start, let’s get all of our dependencies in order. Again, we’re writing Bash scripts, so we’ll be spending time in the command line.

Here are the commands for all of the dependencies we need to start optimizing images:

sudo apt-get update sudo apt-get install imagemagick webp jpegoptim optipng npm install -g svgexport svgo

It’s a good idea to know what we’re working with before we start using them:

OK, we have our images in the original-images directory from the GitHub repo. You can follow along at commit 3584f9b.

Note: It is strongly recommended to backup your images before proceeding. We’re about to run programs that alter these images, and while we plan to leave the originals alone, one wrong command might change them in some irreversible way. So back anything up that you plan to use on a real project to prevent cursing yourself later.

Organize images

OK, we’re technically set up. But before we jump into optimizing all the things, we should organize our files a bit. Let’s organize them by splitting them up into different sub-directories based on their MIME type. In fact, we can create a new bash to do that for us!

The following code creates a script called organize-images.sh:

#!/bin/bash  input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  for img in $ ( find $ input_dir -type f -iname "*" ); do   # get the type of the image   img_type=$ (basename `file --mime-type -b $ img`)    # create a directory for the image type   mkdir -p $ img_type    # move the image into its type directory   rsync -a $ img $ img_type done

This might look confusing if you’re new to writing scripts, but what it’s doing is actually pretty simple. We give the script an input directory where it looks for images. the script then goes into that input directory, looks for image files and identifies their MIME type. Finally, it creates subdirectories in the input folder for each MIME type and drops a copy of each image into their respective sub-directory.

Let’s run it!

bash organize-images.sh original-images

Sweet. The directory looks like this now. Now that our images are organized, we can move onto creating variants of each image. We’ll tackle one image type at a time.

Convert to PNG

We will convert three types of images into PNG in this tutorial: WebP, JPEG, and SVG. Let’s start by writing a script called webp2png.sh, which pretty much says what it does: convert WebP files to PNG files.

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  # for each webp in the input directory for img in $ ( find $ input_dir -type f -iname "*.webp" ); do   dwebp $ img -o $ {img%.*}.png done

Here’s what happening:

  • input_dir="$ 1": Stores the command line input to the script
  • if [[ -z "$ input_dir" ]]; then: Runs the subsequent conditional if the input directory is not defined
  • for img in $ ( find $ input_dir -type f -iname "*.webp" );: Loops through each file in the directory that has a .webp extension.
  • dwebp $ img -o $ {img%.*}.png: Converts the WebP image into a PNG variant.

And away we go:

bash webp2png.sh webp

We now have our PNG images in the webp directory. Next up, let’s convert JPG/JPEG files to PNG with another script called jpg2png.sh:

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  # for each jpg or jpeg in the input directory for img in $ ( find $ input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" ); do   convert $ img $ {img%.*}.png done

This uses the convert command provided by the ImageMagick package we installed. Like the last script, we provide an input directory that contains JPEG/JPG images. The script looks in that directory and creates a PNG variant for each matching image. If you look closely, we have added -o -iname "*.jpeg" in the find. This refers to Logical OR, which is the script that finds all the images that have either a .jpg or .jpeg extension.

Here’s how we run it:

bash jpg2png.sh jpeg

Now that we have our PNG variants from JPG, we can do the exact same thing for SVG files as well:

#!/bin/bash  # directory containing images input_dir="$ 1"  # png image width width="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ width" ]]; then   echo "Please specify image width."   exit 1 fi  # for each svg in the input directory for img in $ ( find $ input_dir -type f -iname "*.svg" ); do   svgexport $ img $ {img%.*}.png $ width: done

This script has a new feature. Since SVG is a scalable format, we can specify the width directive to scale our SVGs up or down. We use the svgexport package we installed earlier to convert each SVG file into a PNG:

bash svg2png.sh svg+xml

Commit 76ff80a shows the result in the repo.

We’ve done a lot of great work here by creating a bunch of PNG files based on other image formats. We still need to do the same thing for the rest of the image formats before we get to the real task of optimizing them.

Convert to JPG

Following in the footsteps of PNG image creation, we will convert WebP, JPEG, and SVG into JPG. Let’s start by writing a script called png2jpg.sh that converts PNG to SVG:

#!/bin/bash  # directory containing images input_dir="$ 1"  # jpg image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each png in the input directory for img in $ ( find $ input_dir -type f -iname "*.png" ); do   convert $ img -quality $ quality% $ {img%.*}.jpg done

You might be noticing a pattern in these scripts by now. But this one introduces a new power where we can set a -quality directive to convert PNG images to JPG images. Rest is the same.

And here’s how we run it:

bash png2jpg.sh png 90

Woah. We now have JPG images in our png directory. Let’s do the same with a webp2jpg.sh script:

#!/bin/bash  # directory containing images input_dir="$ 1"  # jpg image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each webp in the input directory for img in $ ( find $ input_dir -type f -iname "*.webp" ); do   # convert to png first   dwebp $ img -o $ {img%.*}.png    # then convert png to jpg   convert $ {img%.*}.png -quality $ quality% $ {img%.*}.jpg done

Again, this is the same thing we wrote for converting WebP to PNG. However, there is a twist. We cannot convert WebP format directly into a JPG format. Hence, we need to get a little creative here and convert WebP to PNG using dwebp and then convert PNG to JPG using convert. That is why, in the for loop, we have two different steps.

Now, let’s run it:

bash webp2jpg.sh jpeg 90

Voilà! We have created JPG variants for our WebP images. Now let’s tackle SVG to JPG:

#!/bin/bash  # directory containing images input_dir="$ 1"  # jpg image width width="$ 2"  # jpg image quality quality="$ 3"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ width" ]]; then   echo "Please specify image width."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each svg in the input directory for img in $ ( find $ input_dir -type f -iname "*.svg" ); do     svgexport $ img $ {img%.*}.jpg $ width: $ quality% done

You might bet thinking that you have seen this script before. You have! We used the same script for to create PNG images from SVG. The only addition to this script is that we can specify the quality directive of our JPG images.

bash svg2jpg.sh svg+xml 512 90

Everything we just did is contained in commit 884c6cf in the repo.

Convert to WebP

WebP is an image format designed for modern browsers. At the time of this writing, it enjoys roughly 90% global browser support, including with partial support in Safari. WebP’s biggest advantage is it’s a much smaller file size compared to other mage formats, without sacrificing any visual quality. That makes it a good format to serve to users.

But enough talk. Let’s write a png2webp.sh that — you guessed it — creates WebP images out of PNG files:

#!/bin/bash  # directory containing images input_dir="$ 1"  # webp image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each png in the input directory for img in $ ( find $ input_dir -type f -iname "*.png" ); do   cwebp $ img -q $ quality -o $ {img%.*}.webp done

This is just the reverse of the script we used to create PNG images from WebP files. Instead of using dwebp, we use cwebp.

bash png2webp.sh png 90

We have our WebP images. Now let’s convert JPG images. The tricky thing is that there is no way to directly convert a JPG files into WebP. So, we will first convert JPG to PNG and then convert the intermediate PNG to WebP in our jpg2webp.sh script:

#!/bin/bash  # directory containing images input_dir="$ 1"  # webp image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each webp in the input directory for img in $ ( find $ input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" ); do   # convert to png first   convert $ img $ {img%.*}.png    # then convert png to webp   cwebp $ {img%.*}.png -q $ quality -o $ {img%.*}.webp done

Now we can use it like this to get our WebP variations of JPG files:

bash jpg2webp.sh jpeg 90

Commit 6625f26 shows the result.

Combining everything into a single directory

Now that we are done converting stuff, we’re one step closer to optimize our work. But first, we’re gong to bring all of our images back into a single directory so that it is easy to optimize them with fewer commands.

Here’s code that creates a new bash script called combine-images.sh:

#!/bin/bash  input_dirs="$ 1" output_dir="$ 2"  if [[ -z "$ input_dirs" ]]; then   echo "Please specify an input directories."   exit 1 elif [[ -z "$ output_dir" ]]; then   echo "Please specify an output directory."   exit 1 fi  # create a directory to store the generated images mkdir -p $ output_dir  # split input directories comma separated string into an array input_dirs=($ {input_dirs//,/ })  # for each directory in input directory for dir in "$ {input_dirs[@]}" do   # copy images from this directory to generated images directory   rsync -a $ dir/* $ output_dir/ done

The first argument is a comma-separated list of input directories that will transfer images to a target combined directory. The second argument is defines that combined directory.

bash combine-images.sh jpeg,svg+xml,webp,png generated-images

The final output can be seen in the repo.

Optimize SVG

Let us start by optimizing our SVG images. Add the following code to optimize-svg.sh:

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then echo "Please specify an input directory." exit 1 fi  # for each svg in the input directory for img in $ ( find $ input_dir -type f -iname "*.svg" ); do   svgo $ img -o $ {img%.*}-optimized.svg done

We’re using the SVGO package here. It’s got a lot of options we can use but, to keep things simple, we’re just sticking with the default behavior of optimizing SVG files:

bash optimize-svg.sh generated-images
This gives us a 4KB saving on each image. Let’s say we were serving 100 SVG icons — we just saved 400KB!

The result can be seen in the repo at commit 75045c3.

Optimize PNG

Let’s keep rolling and optimize our PNG files using this code to create an optimize-png.sh command:

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  # for each png in the input directory for img in $ ( find $ input_dir -type f -iname "*.png" ); do   optipng $ img -out $ {img%.*}-optimized.png done

Here, we are using the OptiPNG package to optimize our PNG images. The script looks for PNG images in the input directory and creates an optimized version of each one, appending -optimized to the file name. There is one interesting argument, -o, which we can use to specify the optimization level. The default value is 2 **and values range from 0 to 7. To optimize our PNGs, we run:

bash optimize-png.sh generated-images
PNG optimization depends upon the information stored in the image. Some images can be greatly optimized while some show little to no optimization.

As we can see, OptiPNG does a great job optimizing the images. We can play around with the -o argument to find a suitable value by trading off between image quality and size. Check out the results in commit 4a97f29.

Optimize JPG

We have reached the final part! We’re going to wrap things up by optimizing JPG images. Add the following code to optimize-jpg.sh:

#!/bin/bash  # directory containing images input_dir="$ 1"  # target image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each jpg or jpeg in the input directory for img in $ ( find $ input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" ); do   cp $ img $ {img%.*}-optimized.jpg   jpegoptim -m $ quality $ {img%.*}-optimized.jpg done

This script uses JPEGoptim. The problem with this package is that it doesn’t have any option to specify the output file. We can only optimize the image file in place. We can overcome this by first creating a copy of the image, naming it whatever we like, then optimizing the copy. The -m argument is used to specify image quality. It is good to experiment with it a bit to find the right balance between quality and file size.

bash optimize-jpg.sh generated-images 95

The results are shows in commit 35630da.

Wrapping up

See that? With a few scripts, we can perform heavy-duty image optimizations right from the command line, and use them on any project since they’re installed globally. We can set up CI/CD pipelines to create different variants of each image and serve them using valid HTML, APIs, or even set up our own image conversion websites.

I hope you enjoyed reading and learning something from this article as much as I enjoyed writing it for you. Happy coding!


The post Converting and Optimizing Images From the Command Line appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

What I Learned by Fixing One Line of CSS in an Open Source Project

I was browsing the Svelte docs on my iPhone and came across a blaring UI bug. The notch in the in the REPL knob was totally out of whack. I’m always looking to contribute to open source, and I thought this would be a quick and easy fix. Turns out, there was a lot more to it than just changing one line of CSS. 

Replicating, debugging, setting up the local environment was interesting, difficult, and meaningful.

The issue

I opened my browser DevTools, thinking I’d see the same issue in the phone view. But, the bug wasn’t there. Now this is a seriously tricky CSS problem.

💡 What I learned

If you’re using Chrome on iOS as your browser, you’re still using Safari’s renderer. From Wikipedia:

Chrome uses the iOS WebKit – which is Apple’s own mobile rendering engine and components, developed for their Safari browser – therefore it is restricted from using Google’s own V8 JavaScript engine.

This is backed up by caniuse, which provides this note on iPS Safari:

Screenshot of caniuse with a note saying the safari browser for iOS is tried to the operating system, so the numbers used are based on the OS version.

Now it’s clear why the issue wasn’t showing up on my machine but it was showing up on my phone. Different rendering engines! 

Reproduce the issue locally

I pulled down the project and ran it locally. I confirmed it was still an issue by running the local code in a simulator as well as on my actual iPhone. Safari on macOS has an easy way to open up DevTools instances of each one.

Screenshot of the Safari Develop menu expanded with the Simulator option highlighted in blue.

This provides access to a console just like you would in the browser but for iOS Safari.  I’m not going to lie, Apple’s developer experience is top notch (see what I did there? 😬).

I’m able to reproduce the issue locally now.

💡 What I learned

After pulling down the Svelte repo and looking around the code a bit, I noticed the UI and SVGs were being pulled in via a package called @sveltejs/site-kit. Great, now I need my local version of site kit to get pulled into svelte/site so I can see changes and debug the issue.

I needed to point the node_modules in Svelte’s package.json to my local copy of site-kit. This sounded like a Symlink. After looking through the docs without much luck I Googled around and stumbled upon npm-link. That let me see what I was doing!

I can now make local changes to site-kit and see them reflected in the Svelte project.

Solving the issue

Seriously, all this needed was a one-line change:

border: transparent;

But locating where that one line should go was not as straightforward as you’d think. Source maps on the project are still a little rough around the edges and are showing this CSS coming from the Nav.svelte component when it was really coming from another file. That would be another great way to contribute to the project!

Then you search around and learn that this is being handled and you learn a little more about how it’s done. Everything now looks great on mobile and desktop.

That’s all it needed!

Let’s rewind

What started as a quick, one-line change became a bit of a journey. I had to:

  • Run the project and component repositories
  • Learn about system linking
  • Contribute documentation about lining to site-kit
  • Learn about different browser renderers
  • Learn how to emulate an iOS Safari browser
  • Learn how to get access to its debugger
  • Find the issue when source maps weren’t working correctly
  • Fix the issue (finally!)

Working on your own, you normally don’t get to deal with issues like this, or have a large complex system you need to build a mental model of and learn. You don’t get to learn from maintainers. Most importantly, you don’t see all of the hard work that goes into building a popular technical product.

When I submitted this idea to CSS-Tricks. Chris said he had recently dealt with a similar situation. Difficult learning is durable learning. Embrace the struggle.

Never stop learning

I grabbed another issue from the Svelte project and now I’m learning about CSSStyleSheet because there’s another issue (I think), with how Safari handles keyframe animations within stylemanager.ts. And so the learning continues down paths I never would have treaded in my day-to-day work.

When something breaks, enjoy the journey of learning the system. You’ll gain valuable insights into why that thing broke and what can be done to fix it. That’s one of the awesome benefits of contributing to open source projects and why I’d encourage you to do the same.


The post What I Learned by Fixing One Line of CSS in an Open Source Project appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

10 modern layouts in 1 line of CSS

, ,
[Top]

When a Line Doesn’t Break

We expect a line to break when the text on that line reaches the parent box boundaries. We see this every time we create a paragraph, just like this one. When the parent box doesn’t have enough room for the next word in a line, it breaks it and moves down to the next line and repeats that process.

Well, that’s how it works when words are separated by spaces or other whitespace. As far as CSS goes, there are five (!) properties that can possibly affect how and when a line breaks. Let’s not get into all that again though. Let’s instead look at a situation where it seems like a line is going to break but it doesn’t as an excuse to learn a little something about line breaking.

What happens in a situation where there is no way for the browser to know when it’s OK to make a break?

Let’s get ourselves into a bad situation with a “tag list” and then get ourselves out of it. Here’s our markup:

<ul>   <li>PHP</li>   <li>JavaScript</li>   <li>Rust</li>   <!-- etc. --> </ul>

Next, we’ll apply CSS that overwrites the default list style from its default vertical orientation to horizontal by making the list items display inline.

ul {   margin: 0;   padding: 0;   list-style: none; } 
 li {   display: inline;   padding-right: 5px; }

The list looks just like we want it. I added a little bit of space between one list item and another, so it doesn’t look too crowded.

Now let’s introduce a wrapper element to the mix. That’s essentially a div around the unordered list. We can give it a class, say, .tags.

<div class="tags">   <ul>     <li>PHP</li>     <li>JavaScript</li>     <li>Rust</li>   </ul> </div>

Let’s say we want to give the wrapper a fixed width of 200px. That’s where we should expect to see line breaks happen as the unordered list bumps into wrapper’s boundaries.

.tags {   width: 200px; }

Here comes the interesting part. Many people use minifiers in their build process to reduce file sizes by getting rid of unnecessary values. Some of these values are whitespaces, which includes spaces, tabs, and line breaks (such as carriage return and line feed) characters that are use for formatting purposes but considered by minifies to be irrelevant to the final result.

If we “minify” our HTML by removing new lines, then this is what we get:

<div class="tags"><ul><li>PHP</li><li>JavaScript</li><li>Rust</li></ul></div>

UH OH. As you can see, the list is not breaking at the 200px boundary anymore. Why? What is different now? Personally, I thought HTML didn’t care about whitespaces. What is so different about the minified version from the original markup?

The browser does actually care about whitespaces… but only sometimes. And this just so happens to be one of those instances. When the page is being parsed, the parser sees this list as one long word because, from its perspective, there are no characters that differentiate one from the other.

One might think having the padding is affecting things. But if we remove the padding from our list items we still get the same result… only with no spacing between items.

The browser sees the entire list as a single word.

We can get natural line breaks from special characters

Besides spaces, excluding non-breaking spaces (&nbsp;), there are some other characters that will force a line break, including:

  • After hypen (‐)
  • After en dash ()
  • Before and after em dash ()
  • After question mark (?)
  • Zero-width white space (U+200B or &#8203;)

These line breaks happen at rendering time which means the browser still sees this as one long word. Adding a new list item to the tag list with any of these characters will force a line break. Let’s add “Objective-C” to the list. Its name contains a hyphen, which we can use to see how it affects things.

For better readability purpose the code code will have indentation and new line.

<div class="tags">   <ul>     <li>PHP</li>     <li>JavaScript</li>     <li>Rust</li>     <li>Objective-C</li>   </ul> </div>

That’s good. Let’s look at three solutions to our non-line-breaking list along these lines.

Solution 1: Add one of the breaking characters

We can keep forcing line breaks with those breaking characters like we just did. But remember, if you are using a minifier, adding the spaces in or after the closing tag won’t guaranteed it won’t be removed, as not all minifiers work the same way.

<div class="tags">   <ul>     <li>PHP </li>     <li>JavaScript </li>     <li>Rust </li>     <li>Objective-C </li>   </ul> </div>

Solution 2: Use pseudo-elements

The breaking character can also be added using the ::before and ::after pseudo-elements in CSS. What makes this solution effective is that it’s not affected by an HTML minifier because the whitespace is added when the CSS is applied.

But, before we move on, let’s talk a moment about collapsing whitespaces.

The browser collapses whitespace before and after a character that forces a line break inside inline elements. With this in mind, there’s a little trick to using ::after and the content property with whitespacing and display: inline-block. The inline-block element adds a breaking character at the end of the text. Then the content property space comes after the breaking character created by the inline-block element, which results in the space being removed at rendering time. That is, unless the white-space property is set to pre.

Solution 3: Use inline-block instead

Perhaps you have bumped into a fight with space between inline-block elements in CSS before. We can use the inline-block value on the display  property to force a line break because the inline-block element already has the extra whitespace we need. This works similar to adding a zero-width space character, but the list items will have no visual separation.

Solution 4: Use flex or inline-flex

Another solution is to define the unordered list as a flex container, which allows us to use flex-flow to set the direction of the list and to make sure it to multiple lines when needed.

We can also turn to the display: inline-flex instead of inline-block solution. The idea here is that the entire flexible container display inline.


So, we started this post with a situation that might come up when using a minifier. That said, minifiers — and many libraries for that matter — are smart and will try to prevent this line-breaking issue from happening.

Sure, it’s not an extremely common situation to bump into. It’s really one of those things that can fly under the radar if we’re not paying attention but, if it does happen, at least we know there are ways to work around it.

The post When a Line Doesn’t Break appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

How to Tame Line Height in CSS

In CSS, line-height is probably one of the most misunderstood, yet commonly-used attributes. As designers and developers, when we think about line-height, we might think about the concept of leading from print design — a term, interestingly enough, that comes from literally putting pieces of lead between lines of type. 

Leading and line-height, however similar, have some important differences. To understand those differences, we first have to understand a bit more about typography. 

An overview of typography terms

In traditional Western type design, a line of text is comprised of several parts: 

  • Baseline: This is the imaginary line on which the type sits. When you write in a ruled notebook, the baseline is the line on which you write.
  • Descender: This line sits just below the baseline. It is the line that some characters — like lowercase g, j, q, y and p  — touch below the baseline. 
  • X-height: This is (unsurprisingly) the height of a normal, lowercase x in a line of text. Generally, this is the height of other lowercase letters, although some may have parts of their characters that will exceed the x-height. For all intents and purposes, it servers as the perceived height of lowercase letters.
  • Cap-height: This is the height of most capital letters on a given line of text.
  • Ascender: A line that oftentimes appears just above the cap height where some characters like a lowercase h or b might exceed the normal cap height.
Illustrating the ascender, cap height, x-height, baseline and descender of the Lato font with The quick fox as sample text.

Each of the parts of text described above are intrinsic to the font itself. A font is designed with each of these parts in mind; however, there are some parts of typography that are left up to the type setter (like you and me!) rather than the designer. One of these is leading.

Leading is defined as the distance between two baselines in a set of type.

Two lines of text with an order box around the second line ofd text indicating the leading.

A CSS developer might think, “OK, leading is the line-height, let’s move on.” While the two are related, they are also different in some very important ways.

Let’s take a blank document and add a classic “CSS reset” to it:

* {   margin: 0;   padding: 0; }

This removes the margin and padding from every single element.

We’ll also use Lato from Google Fonts as our font-family.

We will need some content, so let’s an create an <h1> tag with some text and set the line-height to something obnoxiously huge, like 300px. The result is a single line of text with a surprising amount of space both above and below the single line of text.

When a browser encounters the line-height property, what it actually does is take the line of text and place it in the middle of a “line box” which has a height matching the element’s line-height. Instead of setting the leading on a font, we get something akin to padding one either side of the line box.

Two lines of text with orange borders around each line of text, indicating the line box for each line. The bottom border of the first line and the top border of the second line are touching.

As illustrated above, the line box wraps around a line of text where leading is created by using space below one line of text and above the next. This means that for every text element on a page there will be half of the leading above the first line of text and after the last line of text in a particular text block.

What might be more surprising is that explicitly setting the line-height and font-size on an element with the same value will leave extra room above and below the text. We can see this by adding a background color to our elements.

This is because even though the font-size is set to 32px, the actual text size is something less than that value because of the generated spacing.

Getting CSS to treat line-height like leading

If we want CSS to use a more traditional type setting style instead of the line box, we’ll want a single line of text to have no space either above or below it — but allow for multi-line elements to maintain their entire line-height value. 

It is possible to teach CSS about leading with a little bit of effort. Michael Taranto released a tool called Basekick that solves this very issue. It does so by applying a negative top margin to the ::before pseudo-elementand a translateY to the element itself. The end result is a line of text without any extra space around it.

The most up-to-date version of Basekick’s formula can be found in the source code for the Braid Design System from SEEK. In the example below, we are writing a Sass mixin to do the heavy lifting for us, but the same formula can be used with JavaScript, Less, PostCSS mixins, or anything else that provides these kinds of math features.

@function calculateTypeOffset($ lh, $ fontSize, $ descenderHeightScale) {   $ lineHeightScale: $ lh / $ fontSize;   @return ($ lineHeightScale - 1) / 2 + $ descenderHeightScale; } 
 @mixin basekick($ typeSizeModifier, $ baseFontSize, $ descenderHeightScale, $ typeRowSpan, $ gridRowHeight, $ capHeight) {   $ fontSize: $ typeSizeModifier * $ baseFontSize;   $ lineHeight: $ typeRowSpan * $ gridRowHeight;   $ typeOffset: calculateTypeOffset($ lineHeight, $ fontSize, $ descenderHeightScale);   $ topSpace: $ lineHeight - $ capHeight * $ fontSize;   $ heightCorrection: 0;      @if $ topSpace > $ gridRowHeight {     $ heightCorrection: $ topSpace - ($ topSpace % $ gridRowHeight);   }      $ preventCollapse: 1;      font-size: #{$ fontSize}px;   line-height: #{$ lineHeight}px;   transform: translateY(#{$ typeOffset}em);   padding-top: $ preventCollapse; 
   &::before {     content: "";     margin-top: #{-($ heightCorrection + $ preventCollapse)}px;     display: block;     height: 0;   } }

At first glance, this code definitely looks like a lot of magic numbers cobbled together. But it can be broken down considerably by thinking of it in the context of a particular system. Let’s take a look at what we need to know:

  • $ baseFontSize:This is the normal font-size for our system around which everything else will be managed. We’ll use 16px as the default value.
  • $ typeSizeModifier: This is a multiplier that is used in conjunction with the base font size to determine the font-size rule. For example, a value of 2 coupled with our base font size of 16px will give us font-size: 32px.
  • $ descenderHeightScale: This is the height of the font’s descender expressed as a ratio. For Lato, this seems to be around 0.11.
  • $ capHeight: This is the font’s specific cap height expressed as a ratio. For Lato, this is around 0.75.
  • $ gridRowHeight: Layouts generally rely on default a vertical rhythm to make a nice and consistently spaced reading experience. For example, all elements on a page might be spaced apart in multiples of four or five pixels. We’ll be using 4 as the value because it divides easily into our $ baseFontSize of 16px.
  • $ typeRowSpan: Like $ typeSizeModifier, this variable serves as a multiplier to be used with the grid row height to determine the rule’s line-height value. If our default grid row height is 4 and our type row span is 8, that would leave us with line-height: 32px.

Now we can then plug those numbers into the Basekick formula above (with the help of SCSS functions and mixins) and that will give us the result below.

That’s just what we’re looking for. For any set of text block elements without margins, the two elements should bump against each other. This way, any margins set between the two elements will be pixel perfect because they won’t be fighting with the line box spacing.

Refining our code

Instead of dumping all of our code into a single SCSS mixin, let’s organize it a bit better. If we’re thinking in terms of systems, will notice that there are three types of variables we are working with:

Variable Type Description Mixin Variables
System Level These values are properties of the design system we’re working with. $ baseFontSize
$ gridRowHeight
Font Level These values are intrinsic to the font we’re using. There might be some guessing and tweaking involved to get the perfect numbers. $ descenderHeightScale
$ capHeight
Rule Level These values will are specific to the CSS rule we’re creating $ typeSizeMultiplier
$ typeRowSpan

Thinking in these terms will help us scale our system much easier. Let’s take each group in turn.

First off, the system level variables can be set globally as those are unlikely to change during the course of our project. That reduces the number of variables in our main mixin to four:

$ baseFontSize: 16; $ gridRowHeight: 4;  @mixin basekick($ typeSizeModifier, $ typeRowSpan, $ descenderHeightScale, $ capHeight) {   /* Same as above */ }

We also know that the font level variables are specific to their given font family. That means it would be easy enough to create a higher-order mixin that sets those as constants:

@mixin Lato($ typeSizeModifier, $ typeRowSpan) {   $ latoDescenderHeightScale: 0.11;   $ latoCapHeight: 0.75;      @include basekick($ typeSizeModifier, $ typeRowSpan, $ latoDescenderHeightScale, $ latoCapHeight);   font-family: Lato; }

Now, on a rule basis, we can call the Lato mixin with little fuss:

.heading--medium {   @include Lato(2, 10); }

That output gives us a rule that uses the Lato font with a font-size of 32px and a line-height of 40px with all of the relevant translates and margins. This allows us to write simple style rules and utilize the grid consistency that designers are accustomed to when using tools like Sketch and Figma.

As a result, we can easily create pixel-perfect designs with little fuss. See how well the example aligns to our base 4px grid below. (You’ll likely have to zoom in to see the grid.)

Doing this gives us a unique superpower when it comes to creating layouts on our websites: We can, for the first time in history, actually create pixel-perfect pages. Couple this technique with some basic layout components and we can begin creating pages in the same way we would in a design tool.

Moving toward a standard

While teaching CSS to behave more like our design tools does take a little effort, there is potentially good news on the horizon. An addition to the CSS specification has been proposed to toggle this behavior natively. The proposal, as it stands now, would add an additional property to text elements similar to line-height-trim or leading-trim

One of the amazing things about web languages is that we all have an ability to participate. If this seems like a feature you would like to see as part of CSS, you have the ability to drop in and add a comment to that thread to let your voice be heard.

The post How to Tame Line Height in CSS appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]