Blog

#development

#css

CSS Custom Properties are super powerful. Unfortunately a lot of people are still using them like Sass variables, forgetting that they are scopable and can do some pretty advanced things. This video walks through some cool methodology for making extendable components with a styling API.

Subgrid Changes Everything

For the frontenders out there, you’ve likely seen the news. Subgrid has shipped. It came to Firefox back in 2019, to Safari last year, and now Chrome has shipped it. If you haven’t been following, let me catch you up.

Tables

I got my starts in web development back around the turn of the century. Which was not that long ago. Promise. In those *barf* 20+ years, I have seen every major shift in how we lay out websites. When I started, we used tables still. It was gross, but we had no choice.

See the Pen Table Layout by James Finley (@thefinley) on CodePen.

For a table-based layout, you would start with a table to lay out your columns and rows and then nest child tables until you died in a nest of unmanagable code so you could get close to a complex layout. Luckily we never even considered people being on small screens back then.

Floats

At the end of the 1900’s— I just made a lot of people hate me— we got CSS and were told that it was for styling and format, while HTML was for semantics— describing the text and content of our page. Fair, but it had no true layout system. The closest it had was floats, which were really meant to allow you to have text wrap around an image. But we wanted a 6 column layout. So grid systems like 960.gs and Bootstrap were born to allow us to use utility classes to layout pages. Along with that, we entered an era where vertical was problematic— having all your columns match height in a row required very gross tricks. Oh, and clearfixes. Look it up if you need a refresher.

But, our HTML became a lot cleaner as we got to write less code and allow CSS to do it’s thing.

See the Pen Float Layout by James Finley (@thefinley) on CodePen.

Flexbox

For most of the 2000’s we got used to laying out pages using grid systems built on floats. At the end of the 2000’s, we got our next big shift: flex box. And right in time for the Responsive Web movement. Chrome rolled out support in 2010, Safari in 2008, Firefox in 2006. IE got it with version 10 in 2012, which truly opened us up to use it.

See the Pen Flexbox Layout by James Finley (@thefinley) on CodePen.

Flexbox getting solid support and version usage by 2013-2015 meant we could finally easily center things vertically and horizontally for the first time since tables. Over a decade of CSS and no real layout system, no we had one, but we also had grid right on the horizon.

Grid

IE was first to implement Grid in 2015 to the surprise of everyone. This was when Microsoft really started to show signs of caring for web developers and supporting standards with Edge 12. Where flexbox worked well for layout elements out in one dimension— either column or row— grid was a real, true two-dimensional grid system built right into CSS. March 2017 grid shipped in Chrome, Firefox, and Safari. All at once within weeks of each other.

See the Pen Grid Layout by James Finley (@thefinley) on CodePen.

Instead of needing complex utility functions that did math, we now could tell a cell in a grid to start at column 1 and span 3 columns. And this has been are last 6 years of web development. I’ve been in this game for 20 years. Grid is a beautiful thing.

But.

Subgrid

The original spec, from my understanding, included something called subgrid and it was dropped from the original release. You see in the demo above of grid, the grid can only be used by direct children of the element with display: grid. For grandchildren, they are out of luck. Remember us nesting tables back in the 1900’s? Yeah, we had to go back to that. You have a six-column layout with a cell taking up four columns of that. You want to have four items inside that each take up one column? Yeah, those items don’t know about the grid, so you have to establish a four-column grid on their parent and use the calc function to mastery to get your gaps to align. Subgrid was supposed to allow you to pass the grid onto the children of a grid cell.

And now we are caught up to the present. Subgrid has shipped. Browser usage stats are going up. The demo below will work in anything after Chrome 117, Safari 16, and Firefox 17. Edge will get it soon, from my understanding.

See the Pen Subgrid Layout by James Finley (@thefinley) on CodePen.

Now we can truly lay out pages where everything sits in one grid. Blocks on the page can be logically and semantically separated while not making layout complicated and can still internally rely on one grid.

See the Pen Complex Subgrid Layout by James Finley (@thefinley) on CodePen.

Each of these major eras of web layout have changed the way we think, the amount of code we write, and what designers are able to design. During the float era having three equal height columns was a PITA. Vertically centering something was too. Grid allows some truly complicated layouts that we are still not scratching the surface on, but in many ways it was hampered by the lack of subgrid support for the last six years. Subgrid changes everything once again and I am more than excited for this new era in web layout design.

Something non-designers understandably struggle with is how to make things look good. One of those things is long form content that’s well set and readable. Luckily, CSS makes this easy, you just have to know what to change. That’s exactly what we’re going to teach you in this article.

Set Studio

So many good tips in this article from Andy Bell. I’m using many of them already on this site. Long-form content can be hard to format, so if you need some help, tap the link above!

Scroll Snap for a Nice Mobile Experience

Small enhancement to homepage. Two column layout, so mobile normally would make that one column. Which it had. But I write a few featured posts and lots of “other” posts. The sidebar had the “other” posts, but that got put way down the page. You’re unlikely to get there through scrolling. But putting it first wouldn’t make sense because then the featured posts are too far.

So… I made a scroller using the new scroll-snap CSS functionality. Now you can see the latest “other”, but it doesn’t push the featured posts down 3 scroll heights.

.b--article-list {
	display: flex;
	width: calc(100vw - 64px);
	overflow-x: scroll;
	scroll-snap-type: x mandatory;
	scroll-padding-right: 20%;

	> * {
		min-width: calc(80% - 32px);
		padding-left: 32px;
		scroll-snap-align: start;
	}
}

With scroll-snap-type set to x mandatory, we allow card-based scrolling horizontally. Setting the scroll-snap-align to start for all children makes the snap point the left side of each card.

I have implemented this style of interface before, almost a decade ago. It took a lot of JavaScript. Now with just a few lines of CSS, you get a super interactive little component.

Demo: Homepage view on mobile or scale down and you’ll see the Ramblin’ section using the above.

Clamp for Truly Responsive Typography

CSS’s clamp() has been around for a few years now, but I finally got to use it in the rebuilding of this site. Let’s look at the hero text block on my new homepage!

:root {
  --font-size--dynamic-headline-3: clamp(21px, 4vw, 36px);
  --vertical-spacing-5: clamp(36px, 8vw, 72px);
}

.b--homepage-hero {
  max-width: 60ch;
  margin: 0 auto var(--vertical-spacing-5);
  font-family: Parkly, sans-serif;
  font-size: var(--font-size--dynamic-headline-3);
  text-transform: uppercase;

  & p {
    color: rgb(0 0 0 / 60%);
    text-align: center;

    & strong {
      font-family: "Parkly Wide", sans-serif;
      color: rgb(0 0 0 / 75%);
      text-transform: uppercase;
    }
  }
}

I’ve got the two variables included too. Okay, so the Dynamic Headline 3 variable is defined as clamp(21px, 4vw, 36px). Minimum, preferred, and maximum. The browser then makes the decision for us. Now one has to be mindful of that middle. It can be a somewhat arbitrary relative value if your clamp is tight, but if you have multiple headlines of varying sizes— like any typographer is going to— you need to make these numbers proportionate. Let’s look.

:root {
  --font-size--dynamic-headline-1: clamp(43px, 8vw, 72px);
  --font-size--dynamic-headline-2: clamp(32px, 6vw, 54px);
  --font-size--dynamic-headline-3: clamp(21px, 4vw, 36px);
  --font-size--dynamic-headline-4: clamp(21px, 3.1vw, 28px);
  --font-size--dynamic-headline-5: clamp(21px, 2.65vw, 24px);
  --font-size--dynamic-copy: clamp(16px, 2vw, 18px);
  --font-size--dynamic-small: clamp(14px, 1.78vw, 16px);
}

Dynamic Headline 2 is 50% larger than Dynamic Headline 3. That applies to the min, pref, and max. But at the smallest sizes, Dynamic Headline 3, Dynamic Headline 4, and Dynamic Headline 5 are all the same. They got too small otherwise.

As we allow our pages to be more fluid with dynamic grids, flexbox, and clamps, we have to let go of some of our pixel precision in design. Almost all the fonts— and vertical spacing!— on this site are dynamic with clamp(). The header, I think, is the only thing that isn’t.

Organize Your CSS with Stylelint

A few years ago I was on a new project at work as the frontend engineer. In code reviews I would recommend reordering of CSS properties, as many of us have a preferred order. One of my teammates pointed out that she would never remember my order and thus wouldn’t likely follow it, so I should find a way to automate it.

Stylelint and Stylelint Order enter chatroom.

As I have become accustomed to over the years, most JavaScript engineers are very familiar with ESLint. If you aren’t, please go research and use it. It cleans up your code, makes it consistent, and helps when you are on a larger team than just you. But, frankly, it even helps when it is just you. JavaScript allows chaos. Semicolons are optional. ‘Nough said. What engineers often don’t even think about is linting their CSS. Many don’t consider CSS a “real language,” but that is a different conversation.

Stylelint is exactly what you would expect if you’ve worked with ESLint. I use the stylelint-config-standard to start. This handles most things. I turn off a few items. The biggest part of my config is the custom ordering of properties using stylelint-order.

As you can see below, I separate my properties into groups (eg. display, grid, dimensions). This means position: absolute; is followed by top, which just makes sense. width and height are right by each other. Alphabetical and other sorts work for different people for different reasons, but logically sorting by type of property just keeps me sane.

The nice thing about this is if your run Stylelint, it’ll throw warnings on all things out of order. But, you can run Stylelint with --fix and it will reorder your properties for you.

I bring this with me on every project. The one below is from this very site. A couple days ago I got ESLint and Stylelint working against Astro— I’m used to using it in React projects. I then fixed over 400 linting issues and now feel a peace knowing my code is consistent.

/* You'll need the following packages: stylelint, stylelint-config-standard, stylelint-order */

{
  "extends": "stylelint-config-standard",
  "plugins": [
    "stylelint-order"
  ],
  "rules": {
    "length-zero-no-unit": [true, {"ignore": ["custom-properties"]}],
    "property-no-vendor-prefix": null,
    "custom-property-pattern": null,
    "selector-class-pattern": null,
    "selector-id-pattern": null,
    "at-rule-no-unknown": null,
    "no-descending-specificity": null,
    "order/properties-order": [
      [
        {
          "groupName": "display",
          "properties": [
            "content",
            "appearance",
            "position",
            "top",
            "right",
            "bottom",
            "left",
            "z-index",
            "display",
            "flex",
            "flex-direction",
            "flex-basis",
            "flex-flow",
            "flex-grow",
            "flex-shrink",
            "flex-wrap",
            "align-content",
            "align-items",
            "justify-content",
            "justify-items"
          ]
        },
        {
          "groupName": "grid",
          "properties": [
            "grid",
            "grid-area",
            "grid-template",
            "grid-template-areas",
            "grid-template-rows",
            "grid-template-columns",
            "grid-row",
            "grid-row-start",
            "grid-row-end",
            "grid-column",
            "grid-column-start",
            "grid-column-end",
            "grid-auto-rows",
            "grid-auto-columns",
            "grid-auto-flow",
            "grid-gap",
            "grid-row-gap",
            "grid-column-gap",
            "gap"
          ]
        },
        {
          "groupName": "dimensions",
          "properties": [
            "width",
            "max-width",
            "min-width",
            "height",
            "max-height",
            "min-height"
          ]
        },
        {
          "groupName": "margin and padding",
          "properties": [
            "margin",
            "margin-top",
            "margin-right",
            "margin-bottom",
            "margin-left",
            "padding",
            "padding-top",
            "padding-right",
            "padding-bottom",
            "padding-left"
          ]
        },
        {
          "groupName": "border and background",
          "properties": [
            "box-sizing",
            "border",
            "border-top",
            "border-right",
            "border-bottom",
            "border-left",
            "border-width",
            "border-radius",
            "outline",
            "box-shadow",
            "background",
            "background-color",
            "background-image"
          ]
        },
        {
          "groupName": "font",
          "properties": [
            "font",
            "font-family",
            "font-size",
            "font-style",
            "font-weight",
            "line-height",
            "color",
            "text-decoration",
            "font-smoothing"
          ]
        }
      ],
      {
        "unspecified": "bottom"
      }
    ]
  }
}

View Gist on Github

Two years ago when Web Tools was released, CSS Grid was not supported in its current form by any browser. Now it is supported by all of them. But, in my opinion, unlike most features of CSS that have come before, Grid needs some good tooling around it to help ease work with it in development. So I have started to work on some tools to help with Grid and along the way, I have been dabbling a lot with Grid.

Below is a basic experiment: reproduce the layout of the new App Store on iOS. Along the way, I also found good use for CSS Custom Properties — Variables. I hate that they aren’t called variables. — in setting the background of articles, the background and text color of the text block on each, and more.

CSS is getting more and more powerful. But, along the way, it is getting more and more simple. Where old layout techniques (tables, floats, flex box) were truly ill-suited for layout, CSS Grid is 100% made for layout. So go, dabble with Grid. Grab a comic book and reproduce the layout. Or a poster from the 70’s. Learn the ins-and-outs of it because this is the future of where front-end web development and web design is.

I’m opinionated. If you’ve worked with me or hung out with me for any period of time, you know this. I’ve been building for the web since 2001, before JavaScript libraries, before CSS, before Web Standards. That is a lot of years of experience and with it, one learns a few things. The Standards movement was a major thing just as I was getting started freelancing. jQuery was just coming about as I entered and assisted in teaching a JavaScript class in college. Responsive Web Design was coined and defined as I started my first role at an agency in Chicago.

One learns a few things.

Of the last few years, I have pushed hard against using jQuery. Simply put, most browsers have solid support for new technologies that emulate many of the great features of jQuery. Those native features do it faster, better, and require less code. Remove jQuery and you remove 30+kb of resource load.

I have pushed against Bootstrap. It is a large beast of a library when most sites that include it use it for a grid system and not much else. A grid system can be written in less than 100 lines of CSS that’ll achieve the needs of your site specifically, so why include 200+kb of resources?

I have pushed against WordPress. It’s not good for the developer and offers a not-great experience for the client. There are better CMSs.

So this Summer and Fall, I decided to dabble.

I hadn’t built a website on WordPress in many years. The last one I built was before ACF took off. So I built a client a site in WP and I didn’t hate it. Much of what I have said against WP remains true. Especially after launching a large site on Craft earlier this year. But, for what it does well, it does well. Build a couple of custom plugins was a breeze. ACF made storing complex data from two third-party sources easy and displaying it easier.

I have never used Bootstrap but always maintained that it is best suited for prototyping and administration panels. So as I started building an admin panel for a current project, I decided to try Bootstrap. And I didn’t hate it. There are parts that I won’t touch. The grid system is actually one of them. The spacing utility classes, while useful if you are not writing a lick of CSS, are another. But, the component structure isn’t far from something like Atomic CSS and is actually a welcome thing for the needs of the project. Unfortunately, it requires that the page resources weigh over 300kb, which means I will likely scrap Bootstrap in a few weeks and rewrite with my own CSS.

We must, as developers, not become set in our ways and get dogmatic about certain things. There are things that we should be more opinionated about than others. I wasn’t necessarily wrong with either WordPress or Bootstrap. But, now that I have worked more with each, I can see the lure of them when it comes to getting something done quick and dirty. Atop that, I now have a more solid argument against some points of them.

So if you haven’t dabbled with something in a while or at all, maybe try. You may learn a few things.

But the core principles and mechanisms [of CSS] are no more complicated than they were a decade or even two decades ago. If anything, they’re easier to grasp now, because we don’t have to clutter our minds with float behaviors or inline layout just to try to lay out a page. Flexbox and Grid (chapters 12 and 13, by the way) make layout so much simpler than ever before, while simultaneously providing far more capability than ever before.

Eric Meyer

I built my first couple of layouts with Grid over the last weeks. Hot dog. Things that would have taken me forever with floats took me just a couple lines of code with Grid. I’ve been using Flexbox for a while, to the point of mostly knowing the syntax, but Grid is a brand new beast.

It is astonishing that we have, for almost 20 years of CSS, never had a native layout system. Now we do. Instead of complex (and large) libraries like Bootstrap, we can now do complex layout with simple syntax. Hell, we can do far more complex, asymmetric and two-dimensional layout with Grid that we would never consider doing before. A renaissance in web design is coming. But are front-end developers up to snuff?

The subtle difference between progressive enhancement and graceful degradation is lost on too many. Many of those that support “mobile first” don’t support progressive enhancement, which is an oxymoron.

Mobile first is progressive enhancement. Start with the most important part of any site: content. Mark it up. Semantically. Why? Because some readers experience your site without CSS. It’s true. Some just want the content. Screenreaders and search engines are the largest of these.

Then style the content. That’s the CSS. Mobile first says start with the LCD, the Lowest Common Denominator: mobile. But make sure that your CSS works in the LCD. That might be IE 9. It might be IE 11. Look at your analytics if possible. Progressive enhancement means add features when available. Progressively enhancing. You see? So if you are using a feature that is only available to IE 11 and better (Chrome, Firefox, etc.) and you need to support IE 9, make sure that the feature is not necessary for the user experience.

Then JavaScript. Understand that some have JavaScript disabled. Whether it be over-cautious IT departments, search engines that cannot handle rendering and parsing JavaScript, users with bad network connectivity that fails to load it, or most importantly screenreaders that help those with handicaps consume the content of the web. JavaScript is an enhancement. So long as you have the last two components set, your page works fine when JS is turned off. So, as features are available, use them.

But see, too many developers these days have abandoned this methodology that fueled the Web Standards movement. Instead, sites require over 300 kilobytes of JavaScript libraries and don’t load anything if JavaScript is disabled. Instead of starting with content, they start with jQuery, Angular, React, or whatever the library de jour is. Everything is built in JavaScript. This is graceful degradation, start with the Greatest Common Denominator. Except many aren’t even testing for the LCD. They aren’t turning off JS to make sure that their site will work for those that disable JS. Often they even break the most basic components of the web browser: the URL and the back/forward buttons.

We need to get back to progressive enhancement as the standard. We need to get back to the promise that the web is accessible to all, no matter the device they use.