unModified()

Break stuff. Now.

How You Write CSS Might Be Breaking WYSIWYG Content

All your assumptions are belong to us

November 24, 2016

Whenever we work with content that comes from WYSIWYG editors, there's always feedback from QA about the styling not being quite right. Every time this is brought up, it's just dismissed as a WYSIWYG issue, that the nature of the content is just unpredictable during development. That's just a convenient excuse, and here's why.

The first time I heard this was from our site builders. We had a requirement to allow content editors to add tables and images, and align them at will. I said that this is possible and that the WYSIWYG editor can handle this. I was cut short when they said no. They said that while you can do that in a WYSIWYG editor, you are never sure how it will turn out in the real page.

I followed of their warning and moved on. It made sense that time. Besides, I've had terrible experiences with WYSIWYG myself that it drove me to learning Markdown for regular writing. But something just doesn't feel right. WYSIWYG systems are deterministic. Given the same input and configuration, they will always spit out the same output. The output markup is also very basic. It's just HTML and inline styles, no external JS or CSS required. So what's wrong?

It was a while before this inner conversation came up again. This time, there was a requirement to render lists in WYSIWYG content in the same way we render lists on non-WYSIWYG content, ones whose HTML and CSS we have full control over. They should have custom list bullets and have these bullets render inside the list box instead of the default outside position. The developer that worked on it did something similar to the following in order to apply the style "globally" for both types of content:

ul, ol, dl { /* "fixes" */ }

li, dd, dt { /* "fixes" */ }

It works, it did satisfy the requirements... but it broke everything else. It broke the navigation, tabbing, search results and everything that happens to use lists in their markup. It was no surprise. Using list items for these sort of things makes sense since they're a list of links, tabs, results, everything. The CSS was just leaky and needed refinement. But then...

What if the requirements were the other way around? What if the requirement was to style non-WYSIWYG content in the same way WYSIWYG content was styled? Silly thought, but I was curious and a bit bored. How would I do it? Looks like the same fix would still work. It would still do the same thing, put custom bullets, render them within the box of their lists... and break WYSIWYG content.

Eureka!

The problem wasn't really about unpredictable WYSIWYG content. The real problem was writing leaky CSS. That is, writing styles that unintentionally, but by design, can and will style more than the intended targets. And in this case, the WYSIWYG content is the victim. Yey! We found the real problem. So how do we fix this?

Simple. Stop writing sloppy CSS.

I once described before that CSS is by default a "top-down" system that's similar to classical inheritance. It makes an assumption that everything under it should follow the same suit as the things declared above it. This isn't ideal when sections of the UI can contain multiple, independent, reusable pieces that are unrelated to the container. You would keep breaking them by enforcing styles they never wanted.

To avoid this, writing "bottom-up" similar to compositional systems is much preferred. You build with the small pieces first and only be concerned of that piece's implementation. Larger pieces then become a composition of smaller pieces, still only concerning about itself and never touching the implementation of either its descendants nor its containers to an extent. It doesn't mean the sequence of development becomes bottom-up, it's just the structure of the implementation that's bottom-up.

In the case of a WYSIWYG component, it would merely be a style-less component. That way, it only inherits the very basic CSS. As it gets enclosed by other components that follow the same rule of never touching implementations other than it's own and therefore not leaking CSS, you end up with a WYSIWYG component whose style implementation is never touched by anything other than the theme CSS. And when the theme CSS is written properly, you'll have good-looking WYSIWYG content.

Here's a rough example written in Ractive, but it can apply to any system, component-based or not. I also happen to write in BEM for CSS, a nice way to write CSS but does not really affect the point.

// WYSIWYG editor component
Ractive.components['wysiwyg-editor'] = Ractive.extend({
  template: `
    <div class="wysiwyg-editor">{{{ content }}}</div>
  `,
  css: `
    // CSS only for the wrapper. We don't style the WYSIWYG content at all.
    .wysiwyg-editor{ padding: 5px }
  `,
  data: {
    content: ''
  },
  oninit(){
    // Load up data from store to the content key.
  }
});

// Sidebar component
Ractive.components['sidebar'] = Ractive.extend({
  template: `
    <div class="sidebar">
      <div class="sidebar__item">
        <h3 class="sidebar__header">Weather today</h3>
        <weather-day />
      </div>
      <div class="sidebar__item">
        <h3 class="sidebar__header">Weather today</h3>
        <weather-week />
      </div>
      <div class="sidebar__item">
        <h3 class="sidebar__header">Weather today</h3>
        <wysiwyg-editor />
      </div>
    </div>
  `,
  css: `
    // We only write CSS for items in _this_ component (sidebar). We don't touch
    // the subcomponent CSS at all. This means WYSIWYG content is still
    // unaffected and only inherits the global theme styles.
    .sidebar{ padding: 5px }
    .sidebar__item{ margin-bottom: 15px }
    .sidebar__header{ color: #333 }
  `
});

Conclusion

Stop writing sloppy CSS.

I'm starting to like the word "sloppy" since it imparts force as well a point. The fault isn't the fact that WYSIWYG content is unpredictable. True it is unpredictable in a sense that we can never know what editors put inside them and therefore what comes out. However, the nature of it's output is known, markup and inline CSS. If anything breaks it, it's probably some outside influence, like your sloppy CSS.

So write CSS responsibly, enforce structure onto it.


On on unrelated note, our high school biology professor once told us "An hour of sleep lost is eight hours of sleep to recover". I once thought that it also applies to web development. And so "An hour gained by not doing it right is eight hours of work to refactor it later". Think about it, 7 hours of premium time for spending that extra hour doing it right.