Responsive horizontal scrolling tables for phones in CSS with no additional HTML

For some reason I always forget how to do this, and most tutorials out there suggest wrapping your table in a container <div> tag but as long as you’re using <tbody> (and, ideally, not using <thead> or <tfoot>) then it’s easy with a small bit of CSS and no HTML changes at all.

The trick is to display your <table> as a block, and your <tbody> as a table!

Here’s the code. Fit it into whichever breakpoint makes sense for your site. Mine shown here is the standard phone width breakpoint for WordPress (782 pixels):

@media screen and (max-width: 782px) {

  table {
    display: block;
    overflow-x: auto;
    width: 100%;
  }

  table tbody {
    display: table;
    width: auto;
  }

}

Of course, right after I posted this I found that I had already written about it two years ago with a slightly different solution. Never hurts to have a slightly different perspective.

Internet Explorer and the centered background SVG problem

We are rapidly approaching, I believe, a time when we no longer need to worry about things that don’t work in Internet Explorer.

But we’re not there yet.

I’ve started using SVG images on the web a lot, especially for logos, which I tend to put into a flexible-width container at the top left corner of pages. Which is great, except IE has this annoying habit of resizing the canvas for SVGs to fill the container they’re in. The whole logo appears on the page, and scaled properly… but it’s horizontally centered in the container, rather than flush against the left edge.

There appear to be many possible solutions, but for a solution that is 100% fluid/responsive (that is, it will scale with the container), this does the trick.

It does involve manually editing the SVG code, but it’s simple and you only need to do it once as part of the image prep process. In the <svg> tag, look for the viewbox attribute. It will most likely consist of four numbers… 0 0 followed by the width and height of the canvas. After this attribute, simply add width="x" height="y" with the same x and y values from the viewbox attribute.

It works!

It’s REALLY SIMPLE to add horizontal scroll to a table in CSS

Noted for future reference (by me). Tables aren’t that common on websites anymore, but sometimes they’re legitimately needed (you know, for tabular information).

One thing that I always struggle with is adding horizontal scroll to tables on responsive phone layouts. I know it involves overflow, but for some reason I always make the solution way more complicated than it needs to be.

You don’t need a container element. All you need to do is add these two CSS properties to your tables:

table {
  display: block;
  overflow-x: auto;
}

The StackOverflow post that illuminated the answer also has you add white-space: nowrap; but that can be problematic if your cells have enough text that they need to wrap. I found a better solution was to set a minimum width on cells:

th, td {
  min-width: 120px;
}

You may want to experiment on any given site to see what min-width works best.

A passable but imperfect solution for full-bleed background images on Android and iOS

I’m working on a site right now that has a fixed, full-bleed (i.e. background-size: cover) background image on the <body>. The content flows over it, mostly obscuring it completely, but the background is occasionally revealed in the spaces between content blocks. Some blocks have a semi-transparent background so you can see the fixed background as if through frosted glass.

Here’s the CSS:

body {
  background: rgb(255,255,255) url('../images/ui/body_bg.jpg') center center no-repeat fixed;
  background-size: cover;
}

It’s a cool effect, but it really, really does not want to play nicely on mobile. Various odd things happen on both Android and iOS, and they are completely different.

Quick side note: Yes, the background image is a JPEG. Normally I only use PNG or SVG images in UI elements, but I had good reason to use JPEG here: even though it’s only two colors (with some in-between colors due to antialiasing), the pattern in the background is incredibly complex, and a JPEG version of the file is about 1/5 the size of the PNG. And since it’s an illustration, I tried making an SVG version first, but the pattern is so large that the SVG was about 2 MB! So JPEG it is… which may be a factor in the issue I’m having on Android, but I haven’t tested a PNG version of the image to verify that.

iOS Problems

I’m an iPhone user, so I mainly test responsive sites on iOS. I do own an Android phone (a Motorola Moto E, which I highly recommend as a cheap-but-decent Android phone for testing), but I generally only break it out during the final round of browser testing prior to launching a site.

The issues with background images on iOS are well-known to most web developers. iOS has a number of rather arbitrary seeming limitations imposed upon the Mobile Safari browsing experience, generally for one of three reasons: 1) performance, 2) touch interface usability, 3) the whims of the ghost of Steve Jobs. In the case of background images, background-attachment is not supported. I’m not really sure how this would impact either (1) or (2) — although I think with the early underpowered iPhone generations, it did impact performance — so I think we’re dealing mostly with (3) here. At any rate, because you can’t have an attached background on iOS, I added this in my media queries:

@media screen and (max-width: 782px) {

  body {
    /* For background handling on iOS */
    background-attachment: scroll; background-repeat: repeat;
  }

}

Another quick side note: Why is my phone break point at 782 pixels, you ask? Because that’s where WordPress has its break point for the admin interface. I’m not exactly sure why the WP team chose that number, but why fight it?

Besides the background attachment, there’s also the issue that background-size: cover on a phone is going to make the background image huuuuuuuuuge because it’s scaling it to fit the height of the page content, not the screen size. I initially solved that with background-size: 100%;, since we’re now allowing the background to repeat. As you’ll see, however, that led to problems on Android, so I ended up scrapping it.

Android Problems

Yes, Android has problems. Don’t even get me started! But I wasn’t prepared for this.

I opened the page in Android, and, although the background image was displaying as I expected in terms of size and attachment, it looked… awful. The original source image I am working with is a generous 2400 x 1857 pixels, enough to look reasonably sharp on most displays, even at high resolution. And it looks great on my Mac, great on my iPhone. But on the Android phone it was splotchy and low-res looking… like it had been reduced to 200 pixels and then upscaled (which is maybe what Android is doing, somehow… and here is where I’m wondering if the image being a JPEG is a factor, but that’s just a stab in the dark).

I tried a number of possible solutions, the most obvious being to set exact pixel dimensions for the image. I tried 1200 x 929, basically a “x2” size for high-res devices. Still looked like crap. I even tried setting it to 2400 x 1857, the actual dimensions of the image, and it looked like crap… and I don’t mean pixel-doubled, which is what it actually should be; I mean the same splotchy weirdness I had been seeing at other sizes.

And then I discovered David Chua’s solution:

html {
  /* For background image scaling on Android */
  height:100%; min-height:100%;
}

Yet another quick side note: Here I am not placing this inside a media query. We don’t want to only fix this issue on phone screens. Granted, the iOS solution above needs to work on iPads, too… something I haven’t really solved here. I’m workin’ on it!

This change for Android worked perfectly! By this point I had, temporarily at least, removed the iOS workarounds I mentioned above, so on Android the background image was not only perfectly scaled to the browser window, looking sharp and clean, but it was even fixed-position, just like on desktop!

But… the image was back to being huuuuuuuuuge on iOS. Apparently this html trick for Android does absolutely nothing on iOS, so you’re left trying to find another solution that won’t simultaneously break Android.

An uneasy compromise

It’s not perfect, but I found that if I put both of these tricks together, everything works… the only thing we lose is the fixed-position treatment that Android allows but iOS does not. But the background looks great on both platforms and most importantly, behaves consistently on both.

Here’s the complete code I’m rolling with, for now:

html {
  /* For background image scaling on Android */
  height:100%; min-height:100%;
}

@media screen and (max-width: 782px) {

  body {
    /* For background handling on iOS */
    background-attachment: scroll; background-repeat: repeat;
  }

}

As noted above, this doesn’t really address iPads. A simple solution would be to change the media query to @media screen and (max-width: 1024px), but a) that doesn’t account for the larger iPad Pro and b) it also means a desktop display will lose the proper background effect if the window is smaller than that size. I don’t really have a solution; an adaptive treatment using either server-side or JavaScript-based browser detection would be a consideration, but I don’t really like resorting to that sort of thing for something as basic as this.

It doesn’t help that I recently gave my iPad to my daughter so I don’t currently have a tablet of any kind for testing. That’s about to change as I have a newly ordered Kindle Fire arriving today, but of course that’s not going to give me the answer for an iPad. I can try Responsive Design Mode in desktop Safari, but that’s not always a perfect representation of the quirks of an actual mobile device.

Still… this combined solution for phones is an improvement over the default behavior in both cases.

Responsive images with srcset in WordPress

As a developer, I am somewhat conservative. I believe strongly in the importance of web standards, and I am reluctant to be an early adopter of any new techniques or, even worse, non-standard workarounds for limitations in existing standards. I’d rather live with the limitations until a proper standard — or at least a de facto standard — takes hold.

One of the latest issues to challenge my approach has been responsive images. I’ve settled into a pattern of going with “1.5x” quality images, trying to strike a balance between quality on large high-res displays and a reasonable file size. But it really doesn’t do either very well.

Today’s issue of A List Apart features an exciting article:

Responsive Images in Practice

Yes! In practice! Let’s do this!

There have been a couple of competing proposals for handling responsive image sets, and I am pleased to see the srcset attribute begin to emerge as the winner. The biggest plus it has for me is that it degrades gracefully for older browsers that don’t support it.

Well, before I had even finished reading the article I started thinking about how I could leverage the build-in image sizing mechanism in WordPress to use srcset. I haven’t looked around — I’m sure someone else has already created a plugin that perfectly nails what I am attempting to do here. And, to be honest, I haven’t extensively tested this code yet, although I did drop it into a site I’m currently working on, just to be sure it doesn’t throw a fatal error and that it actually does render the HTML <img> tag as advertised.

function img_with_srcset($attachment_id, $default_size='medium', $echo=true) {
  $output = null;

  // Get image metadata
  $image = wp_get_attachment_metadata($attachment_id);

  // Set upload directory for image URLs
  $upload_dir = wp_upload_dir();
  $upload_url = $upload_dir['baseurl'] . '/' . dirname($image['file']) . '/';

  // Build array of sizes for srcset attribute
  $sizes = array();
  foreach ($image['sizes'] as $size) {
    $sizes[$size['width']] = $upload_url . $size['file'] . ' ' . $size['width'] . 'w';
  }
  
  // Sort sizes, largest first
  krsort($sizes);

  // Get image alt text
  $image_alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);

  // Generate output <img> tag
  if (!empty($sizes)) {
    $output =  '<img srcset="' . implode(', ', $sizes) . '" ' .
               'src="' . $upload_url . $image['sizes'][$default_size]['file'] . '" ' .
               'alt="' . esc_html($image_alt) . '" />';
  }
  
  // Return/echo output
  if ($echo) {
    echo $output;
    return true;
  }
  else {
    return $output;
  }
}

Let’s just examine what’s going on here.

The function takes three input parameters. An attachment ID, a default size (for the old school src attribute), and a boolean for whether to echo the output or just return it.

First, we get the attachment metadata and put it into $image. You can see more about what the wp_get_attachment_metadata() function does here.

Next, we set up the $upload_url variable to be the full base URL to the WordPress uploads directory. That’s because the metadata output only includes the filename of each sized image, not its full URL.

Then we loop through all of the sizes in the metadata output, generating a series of strings containing the image URL and its width, for use in the srcset attribute. We put these into an array because we need to manipulate the list a bit: we need to sort it so the largest images come first, and then later we need to implode() this into a comma-separated string.

Of course we also need the image’s alt text, so we grab that with get_post_meta() which you can read more about here.

Finally, assuming we actually have some size data, we build the <img> tag, complete with srcset attribute! Then we either echo or return it, as determined by the $echo input parameter.

Something else I’d like to try with this is taking it a step further by adding a filter that processes page content, looking for <img> tags, and automatically inserts the appropriate srcset attribute.

There you have it. I welcome anyone who’s reading this to give the function a try in your WordPress site, and let me know how it goes!