Fixing background “bleed” on elements that use CSS3 border-radius property

It’s kind of funny that I never encountered this problem before… it must just be extra-noticeable because of the colors on this particular website I’m working on. Anyway, I found that the nice buttons I was creating with CSS3 border-radius were displaying an ugly “bleed” of the background dark blue color beyond the edges of the white border. No good.

before

A little googling led to Mike Harding’s solution, a simple background-clip property in the CSS. (Yet another of the preponderance of new properties in CSS3 that I’m finding it harder and harder to keep up with.) If w3schools.com is to be believed, the vendor prefix is unnecessary. Let’s just go with this:

background-clip: padding-box;

Ah, that’s better!

after

How not to update a WordPress plugin

The meaning of this post’s title is twofold: 1) how, as a WordPress user, to avoid having a plugin show up in the built-in updater; and 2) how, as a developer, you probably should not approach releasing a major update to your plugin that is incompatible with earlier versions.

The scapegoat here is Elliot Condon’s excellent Advanced Custom Fields, which has become one of my essential go-to plugins for building highly customized WordPress websites for my clients. I don’t mean to pick on Elliot Condon. He’s clearly a tremendously talented developer and I have a ton of respect for his work and what it has allowed me to do in my work.

But I do feel that he handled the 4.0 release of Advanced Custom Fields poorly. I’m not sure the fault is really his, however, as it is just as much or more the fault of how the built-in updater in WordPress works. (Especially since at least some of the changes he made in version 4 were done solely to conform to changes in the official WordPress plugin requirements.)

Here’s the problem: WordPress has a central Plugin Directory that makes it easy to install and update approved plugins. Perhaps too easy. Because if an update of a plugin is made available, and it’s newer than the version you have installed, it appears as an option in the updater. Yes, there are links to information about the update, which you really should read before doing anything else, but it’s all too easy with a few clicks to just run the update and move on.

Most of the time, it just works. Which can be dangerous. Because users — even “experts” like me — come to assume it will always “just work.”

As it happens, the version 4.0 release of Advanced Custom Fields was made available while I was on a weeklong vacation out west with my family, with only my iPad (and SLP’s MacBook Air) along for the ride. And, based on my own recommendations, a client with a relatively newly-launched website went ahead and ran the update. This particular site is heavily dependent on ACF, and the update broke it.

The official ACF website offers a migration guide that makes it (somewhat) easy to convert your existing version 3 implementation to work with version 4. But it’s not a “click-it-and-you’re-done” kind of process. It takes time, and you need to know what you’re doing. Which is nothing like the general experience of using the built-in WordPress updater.

I was able to temporarily solve the problem for this website by rolling back to an earlier version (which required some hunting to locate, given the limited Internet access I had with hotel WiFi). Which leads to my first tip:

How to prevent a WordPress plugin from updating

How does WordPress know when a plugin has an update available? Well, it’s easy for it to check the Plugin Directory to see what the latest version of any given plugin is. But how does it know which version you have installed? Also easy. Each plugin’s main PHP file includes a comment block at the top, with information WordPress parses both to display in the Plugins area of the admin interface and for added functionality (e.g. knowing when there’s an update available). For Advanced Custom Fields, the comment block looks like this:

/*
Plugin Name: Advanced Custom Fields
Plugin URI: http://www.advancedcustomfields.com/
Description: Fully customise WordPress edit screens with powerful fields. Boasting a professional interface and a powerfull API, it’s a must have for any web developer working with WordPress. Field types include: Wysiwyg, text, textarea, image, file, select, checkbox, page link, post object, date picker, color picker, repeater, flexible content, gallery and more!
Version: 3.5.8.2
Author: Elliot Condon
Author URI: http://www.elliotcondon.com/
License: GPL
Copyright: Elliot Condon
*/

This is essentially a set of key/value pairs. Version is what WordPress reads to see which version you’re running, and compares it against the master version in the Plugin Directory to see if updates are available.

So how do you keep it from running the update? Simple: Change the version number to something higher. I like to prepend it with “999” so I still know what the “real” version number is that I’m running, like this: 999.3.5.8.2. This way the WordPress updater thinks the major version is “999”, which is almost certainly higher than whatever the real current version is.

Simply change this version number, save the PHP file on your server, and you’re done. The updater will never trigger for this plugin (as long as its real version number is less than 999). You may also want to update Description with an explanation of what you’ve done.

Bear in mind this is a temporary solution. You really should do whatever you need to do to get your site compatible with the latest version, then restore the original version number and let the updater do its magic.

How, as a developer, not to create this mess for your users

I have submitted a few, very simple, plugins and themes to the official WordPress repositories, so I have a bit of experience with this, but I’m no expert on the process. However, what is clear to me is that if you submit changes to a plugin as an update, the built-in updater will pick it up and make it available to any users who have an older version installed. This is dangerous. If your new version includes such radical changes as to make it incompatible with earlier versions, you have to assume that most users will not read your notes, and will believe they can just run the updater with no problems… especially if you’ve made a habit of releasing frequent incremental updates to the plugin in the past that “just worked.”

The only real solution I see to this is to submit the new, incompatible version to the Plugin Directory as an altogether new plugin, instead of an update to the existing plugin. The risk here is that you lose visibility. Your download count and ratings/reviews reset to zero, and anyone who’s using an older version may never know about the new version. So, it’s bad for marketing.

But an incompatible update breaking sites for unsuspecting users is bad for marketing too. It’s going to cause your ratings to take a hit, cause a lot of bad publicity, and turn off your loyal users. The migration is going to be work for them anyway; making them do it after they clean up a mess created when they unsuspectingly ran the update is even worse.

A few specifics as pertains to Advanced Custom Fields

Again, I don’t mean to pick on Elliot Condon for his work with Advanced Custom Fields. I will continue to be a loyal (and, yes, paid) user of this plugin. It’s brilliant. Nonetheless, it’s created a hassle for me this week. After returning from my trip, the first work-related thing I did was go through every client site that’s running ACF and apply my “version 999” trick so those clients won’t run the updater until I have a chance to migrate their sites to version 4.

The biggest challenge I had in rolling back was simply getting my hands on the old version. Sure, I had it on some of my other sites, but I access almost every client site via SSH/SFTP, and the hotel I was staying at had port 22 blocked. Luckily the site I needed to fix was one of the few I access via regular FTP, and port 21 was open. But I still needed to get a copy of version 3.x to reinstall on that client’s site.

As I found along the way, the WordPress Plugin Directory hides old versions under the “Developers” tab, where every previous version can be downloaded as a ZIP file or checked out with Subversion. Previous versions of ACF are available here.

Where’s the option to change the “uploads” path in WordPress 3.5?

Thanks for nothing, WordPress.

It’s not often that I complain about anything in WordPress, much less get genuinely angry about something I think is downright stupid, but today it happened.

I build a lot of WordPress sites for clients, and I frequently post them in a staging area on my server. Rather than having dozens of separate installations of WordPress filling up my server’s disk space, I set up a single installation (but with a separate database for each site), with some simple customizations to the wp-config.php file that tell it which database to use based on the domain name.

Please don’t bother to mention that I should “probably” be running WordPress-MU (or MultiSite or whatever they call it now). Ultimately these sites are going to be hosted elsewhere as standalone WordPress sites so I need to keep their databases separate.

One key to making this arrangement work nicely has always been the simple setting in WordPress that allows you to define a special directory for uploads, instead of dumping them all in the main uploads directory. By setting this to a subdirectory under wp-content/uploads named to match the site’s theme folder name, it was easy to keep everything separate.

It’s been a while since I’ve set up a new site on the staging server, at least since before I updated to WordPress 3.5. But I was doing one today, and I had to do a sanity check as I scoured the settings to try to find where in the hell you set the custom uploads path. I was sure it was under Settings → Media but I couldn’t find it there.

Because they took it out.

Yes, they took it out. Why? Well, apparently there’s a rational explanation, but frankly no explanation would satisfy me because it seems incredibly disruptive to take something like this out, even if it was a bad idea to implement it as it was in the first place.

Fortunately, there’s still a way to do it. You just can’t do it in the admin interface anymore; you need to edit the wp-config.php file directly to add a constant. No problem, I’m already doing that anyway.

Here’s what you need:

define('UPLOADS', '/your/custom/path/under/wordpress/root');

If you’re just putting your uploads into a subdirectory under the main uploads directory, like I am, it would look like this:

define('UPLOADS', '/wp-content/uploads/yourdirectory');

Just remember, if you’re doing this because you’re running multiple sites from a single installation like I am, this constant should be set in the same conditional where you’re setting which database the site should use.

Easy. Not as easy as it was, nor as easy as I think it should be, but…

Light, pollution, memory

Light pollution

I remember the first time I ever observed light pollution. I didn’t know what it was, and I’m not sure it even had a name back then.

It was 1993. I was in college, and I was home for Easter. In fact it was early Easter morning. My uncle was staying with us, in my room, which was in the process of becoming the guest room. He always stayed in my room when he stayed with us. Eventually I would stay in that room, as a guest room, not my room, once I was no longer a resident of the house, but a guest.

At the time, though, I was not yet a guest, though no longer quite a resident. Nonetheless, he was visiting, so he got my room and I was relegated to the couch in the family room. The family room, which had been added on in 1987, when I was 13, had two skylights. One was directly above the couch, so when I was lying on the couch I could look directly up at the sky.

When I was growing up, cities, at least the small town in which I grew up (which I always thought of as a city, despite its modest population of 26,210 — which was no longer the population, but had been the population in the 1970 census, and the city could not yet bring itself to acknowledge the loss of over 10% of its population in the subsequent decades, so it still appeared on the signs as you drove into town) had not yet switched over to sodium-based street lights. However this particular small town/city had made the switch in the brief time since I had gone off to college at an even smaller town — one small enough that even I could make no pretense as to its being a “city.”

I awoke in the middle of the night. Technically, the early morning, Easter morning. It was overcast, and as I now know well, in a city illuminated by sodium streetlights on an overcast night, it is never truly dark, never truly nighttime. Instead, the best you get is an eerie orange twilight, which is what I observed for the first time in my life, that early Easter morning in 1993, 20 years ago.

It was perhaps 2 AM, and as I awoke, then arose, and walked to the kitchen to get a better view, I beheld the city aglow in an unnatural orange luminescence, and… well… it freaked the shit out of me. I had never seen anything like it, and I didn’t understand what could be causing it. Being Easter morning, and being highly impressionable, especially to my own half-lucid, half-dreamlike fantasies, I was sure Armageddon, or… something… was nigh.

Of course, it was not. And eventually I made the connection between the reference to sodium lights I’d heard on Sting’s The Soul Cages album with the eerie orange light, which has since become commonplace in my mostly urban adult life, where I am usually far too busy or distracted or just simply tired to bother to look up into the sky at night and think the kinds of existential, philosophical, cosmic, spiritual, infinite thoughts I used to dwell on so much between the ages of 5 and 22.

But tonight, for a brief moment, I lingered at my back door in south Minneapolis, with a glass of scotch in one hand and my iPhone in the other. On that late night/early Easter morning 20 years ago, I’m not sure which of the two would have seemed more out-of-place in my hands. Surely both would be just as out-of-place as apocalyptic paranoia in my 2013 brain. But still, the connection to that moment half a lifetime ago was there, and I was transported back to a place where I can stare into the sky at night, silently, and wonder.

Can a developer use an iPad as their only portable “computer”?

I am at a crossroads in my work situation. Since 2008 I have worked as a freelance web developer, which naturally meant using a laptop as my primary/only computer. I worked mostly from home, but I would frequently go to coffeehouses, and occasionally work on-site at client offices. A portable computer was a must.

The same week that Steve Jobs announced the 11-inch MacBook Air, I went out and purchased one. It was exactly what I wanted: a full-blown Mac, (almost) as small as an iPad (which of course I already owned as well, but mainly used for testing, occasional gaming, and watching all six seasons of Lost in the span of a month on Netflix, not for “real work”). I loved the MacBook Air. I said it was the best Mac I’d ever owned, though I admitted it was a tad underpowered. Enough so that when SLP needed a new computer 6 months later, I gave her my MacBook Air instead and bought myself a new, slightly more powerful version of the same.

That MacBook Air has been my only computer ever since. In fact, shortly after switching to it full-time, I wrote a glowing review of it right here. But last April I moved my business into a storefront studio space. I’m not going to coffeehouses anymore. Now, more often than not, clients come to me instead of the other way around. And all of this time I’ve been sitting at a desk, with that same 11-inch MacBook Air hooked up to an HP 23-inch LCD. (Yes, HP. I may be a self-proclaimed Apple fanboy, but even I can’t justify the expense of one of their Cinema Display monitors.)

It’s in this context that I’ve finally really become aware of the performance limitations of the 2010 MacBook Air. It’s unbearably slow with Adobe Creative Suite apps. It’s even unbearably slow running Panic’s Coda. And no computer today should choke up on what is essentially a glorified text editor. (That said… As much as I love Coda, it does seem bloated and slow almost everywhere I’ve used it. There’s no comparison to the blazing speed of BBEdit, which I also love, but Coda has some features I prefer, so it remains my main coding tool.)

Over the past few months, as my workload has increased and my patience has diminished, I can no longer pretend that the 2010 MacBook Air’s performance is adequate for my needs. I know the 2012 Airs are at least 3 times faster than the one I have, and I’m sure this year’s will be even faster, and maybe even have a Retina display, and therein lies the problem: I’ve been desperately wanting to upgrade my Mac, but I couldn’t bring myself to buy one of the current 11-inch Airs (the only portable I will consider) when they’re getting so close to a refresh.

At the same time, I have a major crunch at work over the next 3 months. I couldn’t afford to wait on my creaky old Air anymore. So last weekend I settled on a compromise, borne of the fact that I almost never touch my MacBook Air outside of the studio anymore. I got a Mac mini for the studio. I went with the more powerful quad-core i7 model, which is rated on Geekbench as at least 6 times faster than my old MacBook Air, and almost twice as fast as the current ones.

I’ve already noticed a huge difference. Adobe Creative Suite is way faster, almost to the point of no longer being infuriating. (But that’s another story.) Coda is still occasionally sluggish, but that may have more to do with the fact that I’m working with files on our local file server over a questionable WiFi connection. I should try putting the files directly on my hard drive to see if it makes a difference.

But now I am faced with a weird dilemma. This is the first desktop computer I’ve owned since the Dell I had back in 2001, and the first Mac desktop since even before that. (It was a Bondi blue G3 tower, if you were wondering.) The dilemma is this: in a world of iPads, where I am already pretty much never touching my MacBook Air outside of work, do I really need a portable Mac at all?

I still have the Air, of course, and have continued to lug it around next to my iPad in my Tom Bihn bag this week. But why? In the two meetings I had this week at client offices, I only used my iPad. Maybe the iPad is really all I need. Maybe?

I have a few months to find out. I won’t consider buying another MacBook Air until the new models are out, so in the meantime I will experiment. I will try only using the iPad for any and all computing tasks outside of the studio. I’ve begun that today, by typing this blog post on it as I sit at the kitchen counter with my Saturday morning coffee. It’s been a bit of a challenge. I gave up on using the WordPress web interface and switched to the (marginally better) dedicated iPad app. And I’ve made lots of typos… some that iOS auto-corrected, some it didn’t, and some false positives it shouldn’t have. (C’mon, iPad… use some context, would ya? Why would anyone ever write “you we’re”?)

The biggest challenge will be if I have to write some actual code. But it’s a far different world for that than it was even a year ago. I have a handful of coding apps on my iPad, though nothing I have could be more valuable than another pair of apps from Panic: Diet Coda (great name, BTW) and Prompt, a terminal app. I haven’t had much call to use either of them… yet. But I’ve been comforted knowing they’re there.

At the end of this month we’re planning a family vacation to Utah. That may prove to be the ultimate test. Do I dare leave for a week with only my iPad? Honestly, I’m not sure I can. It will depend on the state of my various work projects at that time. But I’d like to be able to give it a try.

I’ll post follow-ups here as the experiment continues.

Building a centered gallery grid with flexible column count for responsive web pages

It took an untold number of fruitless Google searches and a couple of hours of trial and error to get this to work. I think part of the problem may have been that I simply didn’t really know how to describe what I’m trying to do in a way that would yield good search results. And so, I hope now that I have a solution, sharing it here might help someone else.

The situation: I have a web page that contains a gallery of square images. The page is responsive but the sizes of the images are fixed. I want the page to automatically show as many images across as will fit in the layout on any particular screen, creating anywhere from one to five columns as needed. And, it needs to stay centered.

I got all of this going pretty easily… all except the “it has to stay centered” part. I was able to get it to work if there was only a single line of images, but as soon as they wrapped to multiple lines, the container element went to a full width and the images became left-aligned. It took considerable effort to discover a solution, although that solution itself is embarrassingly simple. I was hung up on a couple of possible approaches that got me nowhere, which probably contributed to the problems I had finding the right way to do it.

So… here we go. We’ll start with an unordered list:

<ul class="gallery">
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
  <li><img src="image.jpg" alt="" /></li>
</ul>

And here is that embarrassingly simple CSS:

ul.gallery {
  text-align: center;
}

ul.gallery li {
  display: inline-block;
}

OK, that’s not really all of the CSS. Your li tag needs height and width properties, and you may want to give it margin as well. But those values are going to be specific to your project.

TinyMCE and the non-breaking space problem

Let’s get right to it then: TinyMCE is great, but I am annoyed by its willingness to take users’ multiple spaces literally! Collapsing multiple spaces is a basic characteristic of HTML, and allowing users to carelessly (or intentionally, which they still shouldn’t do) insert multiple spaces by converting every other one of those spaces into the &nbsp; (non-breaking space) character is BAD!

IMHO.

Anyway… start with a pinch of Stack Overflow, add a dash of the official TinyMCE documentation, along with a heaping tablespoon of reading between the lines, and I have a working solution to the problem. My installation of TinyMCE now automatically converts any &nbsp; characters in the text back into regular ol’ spaces.

It’s a bit draconian; after all there are legitimate uses for non-breaking spaces. But 95% of the times they’re inserted by TinyMCE are user accidents, and another 4.9% of those times are abuses like faked “tabs” that would be solved better by another approach altogether. (There are reasonable CSS-based solutions that work in some cases, but let’s talk HTML’s need for tabs another time.)

Anyway… here’s the gist of the solution. You need to create a callback function. Here’s mine:

function my_cleanup_callback(type,value) {
  switch (type) {
    case 'get_from_editor':
      // Remove &nbsp; characters
      value = value.replace(/&nbsp;/ig, ' ');
      break;
    case 'insert_to_editor':
    case 'submit_content':
    case 'get_from_editor_dom':
    case 'insert_to_editor_dom':
    case 'setup_content_dom':
    case 'submit_content_dom':
    default:
      break;
  }
  return value;
}

It may look like there’s a lot of extra stuff in here you don’t need; I included all possible values for type inside the switch to be prepared for the future. You do want to check for type == 'get_from_editor' though; otherwise your replace() is going to run under way too many conditions and may cause weird behavior like new paragraphs appearing when you just want to insert new text into an existing one, or browser-generated warnings about leaving the page when you try to save. (I ran into both as I was fine-tuning this.)

Now that you have your callback function, you just need to… you know… call it. That’s done inside tinyMCE.init(). You’ll need to include this line somewhere:

cleanup_callback: 'my_cleanup_callback',

Be sure to check if cleanup_callback is already declared somewhere, and also don’t forget the comma at the end, unless you’re inserting this as the last line.

Once you’ve got it all rolled out to your site, you’ll need to clear your cache. I’ve found TinyMCE’s configuration files can be annoyingly persistent in the browser cache.

Yes… you have correctly observed that I had to use non-breaking spaces myself in this post, to get the indents in the code samples to show. Pay no attention to the man behind the curtain. And remember my complaint about the lack of tab characters in HTML. Another day.

The uncomfortable marriage of the UNIX command line and Mac GUI, and its implications for my sudoers file

I’m a longtime Mac user. A “power user,” you might say. Not so much a power UNIX user, though I do a fair bit of Linux-based command line tomfoolery as part of my job.

But things get ugly when the two come together. At the command line I am a bit too inclined to treat my Mac like a Linux server. It may have UNIX at its core, but it’s not Linux. And Apple has put some effort into de-UNIX-ing it as well. Things you expect to work don’t work the way you expect them to. (Yes, I just wrote that sentence. See what this is doing to my brain???)

For reasons I don’t care to get into, I decided today that I needed to modify the sudoers file on the studio’s Mac mini file server. And in my own inimitable and slightly stupid way, I handled this task as I typically do anything involving changing buried system files, not by struggling through using a command line text editor, but by copying the file to my desktop (where it is magically released from the prison of UNIX file permissions in which Apple has… uh… imprisoned hidden UNIX system files). I edited the file and put it back in the /etc folder where it belongs.

Only problem: in the process, the file’s ownership and permissions got changed. No problem, I thought. I’ll just sudo that sucker. Only problem is, when the permissions on the sudoers file aren’t what the system expects them to be, it doesn’t let anybody sudo anything.

Well… crap.

But then I remembered… Mac GUI solutions to the rescue! I opened up Disk Utility and ran “Repair Disk Permissions.” Problem solved! Apple has saved me from myself.

Now I can go back to my delusion that I am a power user.

Ode to the locker room

Being a runner in Minnesota can be difficult, because it forces you to make one of three choices:

  1. Run outside in subzero weather.
  2. Get a gym membership and run on a track or treadmill for 4-5 months.
  3. Stop running altogether in the winter.

Since #3 is not a viable option, you’re left with either bundling up with many layers and tiptoeing hesitantly along icy sidewalks or park paths with blustery winds buffeting your face, or paying a monthly fee for the privilege of driving to a building and running indoors on a treadmill or (if you’re lucky) a track, a tedious but climate-controlled solution.

Being an uncharacteristically wimpy Minnesotan, I’ve gone with the gym membership. I’m very fortunate, I suppose, to live close to the Midtown YWCA in Minneapolis, where I have access to first-rate facilities including a 1/6 mile indoor track. I loathe running on a treadmill. The track can be tedious, but at least I’m actually moving. And if I pick the right soundtrack, I can even visualize running around Lake Nokomis instead. (I’ve run Nokomis to the sounds of my own The Long Run enough times that I know precisely where I am in relation to the lake as each of the 11 sections of the 40-minute piece comes on.)

But as much as I can trick myself into enjoying (or at least tolerating) indoor running in the winter, there’s one aspect of Y membership that I will never like or be able to reconcile with my desire to be outside and alone when I run: the locker room.

I was not a jock in school. In fact, I was pretty much exactly whatever the opposite of a jock is. So what little time I did spend in a locker room was an exercise in taunting and humiliation (real or imagined, and probably more imagined than I believed at the time). I’m no longer afraid of the locker room. I just don’t like it.

I don’t like how crowded it is. I don’t like having to find a space on a bench to put my stuff while I change, or coming back to the locker room after my run to see someone else has chosen bench space directly in front of my locker.

I don’t like listening to other people whistling in the showers. What is so great about this experience to make them want to whistle their tuneless little non-melodies?

I don’t like people who are too comfortable being naked in the locker room, and I also don’t like people who are too uncomfortable with it. Be naked in the shower, the sauna, and at your locker, but nowhere else. Don’t be afraid to take off your swim trunks in the shower. Conversely, don’t stand at the sink naked while you shave, or at the counter by the hair dryers, reading a newspaper. (It kind of just seems logical to me to cover up certain parts when you’re wielding a razor blade, electronics, or paper. Especially paper.)

I don’t like listening to other people’s conversations, even when I am deliberately eavesdropping. I don’t want to be eavesdropping. I especially don’t like listening to teenagers swear loudly. And get off my lawn.

I don’t like how hot it is in the locker room, and how by the time I’m done drying off after my shower, I’ve started sweating again before I can even put on my shirt.

Given my dislike of winter in general, and especially my dislike of the compromises it requires (like spending so much time on the corollary disliking of myriad characteristics of spending time in the Y locker room), I’ve been asked by certain individuals in my life why I want to live in Minnesota at all.

They just don’t understand.

I’m not sure if it’s the harsh conditions of life in the Upper Midwest, much like the harsh conditions in the Scandinavian countries where many of our ancestors came from, or whether we’re just resentful of how easily our existence is ignored by the rest of the country, but part of the joy of being Minnesotan is to be able to complain about being Minnesotan. For us, to love something is to feel comfortable complaining about it.

Of course, that would suggest that perhaps I really love the locker room. But love and hate are not opposites. The opposite of love is indifference. And whether I love the locker room, or hate it, the one thing I clearly am not is indifferent.

But whatever the reason for my strong feelings, there is one that is stronger than all. Spring can’t get here soon enough.