Skip to content

Styles and Css

Shane Brinkman-Davis edited this page Oct 26, 2016 · 18 revisions

CSS is Ill-Conceived

CSS consists of two major parts. The first is a whole bunch of new properties for DOM Elements. The second is its namesake, “Cascading Style Sheets.” There are many problems with this abstraction, but the main problem lies in its failure to deliver on its primary goal. The key is to realize what CSS is trying to do. The whole point is to reduce repetition. At first pass it seems CSS achieves this, but when you compare it to the alternative, it becomes clear how terribly it misses the mark.

CSS's Whole Point is to be DRY, but it's still a sad, little Swamp-Monster

A popular term for reducing repetition in code is "DRY" - "don't repeat yourself." In programing languages, we have a whole host of tools, refined over decades to achieve this exact goal in the most powerful ways known: functions, objects, modules, arrays, etc... CSS is a terrible abstraction for reducing repetition. It is leaky, non-composable, and global to just name a few problems. These failures would be universally shunned if they were in a programming language.

The Correct Way to Handle Common Element Properties (i.e. “styles”)

  • Use a real programming language (JavaScript counts)
  • Don’t abstract until you need to (YAGNI).
  • Once you have repetition, move the repeated code into a single location and re-use it.
    • Basically, create a “style” module or class.
    • As your styles get more complex, you can use functions, composition and inheritance to ensure clean, DRY, maintainable code.
  • Repeat as necessary.

ArtEngine 'Styles'

ArtEngine has no special support for styles. Instead, you get access to the full power of Javascript, all the Art libraries and any other Javascript library you desire. ArtReact, however, has one important, small feature which makes using "styles" elegant and simple:

  • When constructing a Component or VirtualElement you can pass in one or more property objects. The constructor will merge all the propecrties together.

For example:

{createComponentFactory, TextElement} = require 'art-react'

bodyText = 
  fontFamily: 'sans-serif'
  fontSize: 16

successFontColor = 
  fontColor: 'green'

module.exports = createComponentFactory
  render: ->
    TextElement bodyText, successFontColor,
      text: "Hello, world!"

SSS - Simplified Style Sheets, CSS in an alternate universe

CSS Shortcomings

CSS has two major problems:

  • It is impossible to debug or reason about because of "cascading."
  • Though it reduces repetition in HTML, CSS stylesheets themselves are highly repetitive. CSS has no mechanism for sharing properties among styles.

CSS could have been so much better, so much simpler.

I have a proposal for dramatically simplifying CSS while enhancing it's core purpose of reducing code repetition. Before I get started, I want to state that I think this is still inferior to the pure-code solution I use in the Art.Suite, but it is close.

Extracting CSS from HTML5

Discarding all of CSS except the properties themselves. All the HTML5 style-attributes that actually effect how a page is presented and interacted with stay. All notions of 'classes,' 'cascading,' and '.css' files is discarded. All that is left is the 'style' property and all those style HTML5 style-attributes.

A Simple Class System

  • SSS class definitions consist of:
    • a case-insensitive name
    • zero or more style-attributes with values
    • zero or more mixins, which are by-name references to other classes
  • HTML5 element attributes
    • class: zero or more classes specified in their class property (looks just like CSS)
    • style: zero or more style-attributes with values (looks just like CSS)

Computing styles is simple, explicit and easy to reason about:

# cssMerge uses the existing rules for merging two lists of style attributes.
cssMerge = (styleA, styleB) -> mergedStyle

merge = (styleMaps...) ->
  styleMap = {}
  for map in compactFlatten styleMaps
    cssMerge styleMap, map 
  styleMap

classes = {}
defineClass = (name, mixinClassNames, styleMap) ->
  mixinStyleMaps = (classes[className] for className in mixinClassNames)
  classes[name] = merge mixinStyleMaps, styleMap

computeElementStyle = (elementType, classNames, styleMap) ->
  classStyleMaps = (classes[className] for className in classNames)
  merge defaultStyles[elementType], classStyleMaps, styleMap

Clone this wiki locally