Synopsis

ctr is a CSS framework, but it’s not comparable to a CSS framework like Bootstrap, Foundation, or similar ilk. However, ctr is analogous to a CSS framework in that the primary goal is to expedite CSS development and reduce complexity. On many levels, the best comparison to draw upon is CSS preprocessors like Stylus, Less, Sass, and PostCSS. Many of the concepts preprocessors present such as variables, nesting, and mixins are concepts ctr builds upon and around.

At the most basic level ctr is an Object Oriented CSS framework built on Node.js that leverages Stylus to provide it with a familiar battle-tested CSS preprocessor environment. The data structure employed by ctr is that of a key-value pair Object tree structure that mimics the Document Object Model. In other words, ctr takes one of the most loved features in CSS preprocessors, nesting, and creates a logical and maintainable framework around the concept through Objects. This idea is reminiscent of Nicole Sullivan’s OOCSS architecture and Philip Walton’s OOCSS proposal. And through full-heartedly embracing “Objects” in both structure and application, ctr leverages their inherent properties in a manner that is reusable, flexible, and extensible.

It’s through this Object structure that ctr builds upon and incorporates the best of the best tools and concepts from the CSS preprocessor and Stylus community. This means you can hit the ground running and start composing your CSS styles right out of the box without wasting your precious time with setup costs. Furthermore, ctr introduces new concepts like classes, common centralized style properties, and instance variables. Through the combined application of all the concepts, tools, and features that compose ctr, it can cut CSS development time by 20-70%. But the cherry on top of the cake is that ctr can be constructed in Stylus, Less, Javascript, and YAML. Granted, it may sound like I’m just throwing buzzwords around to sugar coat ctr or wow you, but I don’t know how else to explain it.

A Simple Example

Let’s start your journey off with simplest of examples, a teal box.

HTML

<!-- html -->
<div class="blue-box"></div>
Edit
ctr('.blue-box', {
width: 200px
height: 200px
background: #3498db
})
.blue-box:
width: 200px
height: 200px
background: '#3498db'
.blue-box {
width: 200px;
height: 200px;
background: #3498db;
}
ctr('.blue-box', {
  width: 200px
  height: 200px
  background: #3498db
})
.blue-box {
  width: 200px;
  height: 200px;
  background: #3498db;
}
.blue-box:
  width: 200px
  height: 200px
  background: '#3498db'

Result

Notes

A Simple Progression

Let’s turn up the volume a bit and make a simple button.

HTML

<!-- HTML -->
<div class="blue-button">
Button
</div>
Edit
ctr('.blue-button', {
color: white
height: 50px
font-size: 20px
cursor: pointer
padding: 0 50px
background: #3498db
line-height: 50px
text-align: center
display: inline-block
})
.blue-button:
color: white
height: 50px
font-size: 20px
cursor: pointer
padding: [0, 50px]
background: '#3498db'
line-height: 50px
text-align: center
display: inline-block
.blue-button {
color: #fff;
height: 50px;
cursor: pointer;
font-size: 20px;
padding: 0 50px;
line-height: 50px;
text-align: center;
background: #3498db;
display: inline-block;
}
ctr('.blue-button', {
  color: white
  height: 50px
  font-size: 20px
  cursor: pointer
  padding: 0 50px
  background: #3498db
  line-height: 50px
  text-align: center
  display: inline-block
})
.blue-button {
  color: #fff;
  height: 50px;
  cursor: pointer;
  font-size: 20px;
  padding: 0 50px;
  line-height: 50px;
  text-align: center;
  background: #3498db;
  display: inline-block;
}
.blue-button:
  color: white
  height: 50px
  font-size: 20px
  cursor: pointer
  padding: [0, 50px]
  background: '#3498db'
  line-height: 50px
  text-align: center
  display: inline-block

Result

Button

A Confidence Progression

Let’s turn up the volume a bit and compose a hover state background transition. This is where ctr starts to shine since it configures all the hover state logic automatically.

HTML

<!-- HTML -->
<div class="blue-button-hover">
Button
</div>
Edit
ctr('.blue-button-hover', {
color: white
height: 50px
font-size: 20px
cursor: pointer
padding: 0 50px
background: #3498db
line-height: 50px
text-align: center
display: inline-block
hover: {
color: #f1c40f
background: #9b59b6
}
})
.blue-button-hover:
color: white
height: 50px
font-size: 20px
cursor: pointer
padding: [0, 50px]
background: '#3498db'
line-height: 50px
text-align: center
display: inline-block
hover:
color: '#f1c40f'
background: '#9b59b6'
.blue-button-hover {
color: #fff;
height: 50px;
cursor: pointer;
font-size: 20px;
padding: 0 50px;
line-height: 50px;
text-align: center;
background: #3498db;
display: inline-block;
}
.blue-button-hover:hover {
color: #f1c40f;
background: #9b59b6;
transition-delay: 0s, 0s;
transition-duration: 0.5s, 0.5s;
transition-property: color, background;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
.blue-button-hover:not(:hover) {
transition-delay: 0s, 0s;
transition-duration: 0.5s, 0.5s;
transition-property: color, background;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
ctr('.blue-button-hover', {
  color: white
  height: 50px
  font-size: 20px
  cursor: pointer
  padding: 0 50px
  background: #3498db
  line-height: 50px
  text-align: center
  display: inline-block
  hover: {
    color: #f1c40f
    background: #9b59b6
  }
})
.blue-button-hover {
  color: #fff;
  height: 50px;
  cursor: pointer;
  font-size: 20px;
  padding: 0 50px;
  line-height: 50px;
  text-align: center;
  background: #3498db;
  display: inline-block;
}
.blue-button-hover:hover {
  color: #f1c40f;
  background: #9b59b6;
  transition-delay: 0s, 0s;
  transition-duration: 0.5s, 0.5s;
  transition-property: color, background;
  transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
.blue-button-hover:not(:hover) {
  transition-delay: 0s, 0s;
  transition-duration: 0.5s, 0.5s;
  transition-property: color, background;
  transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
.blue-button-hover:
  color: white
  height: 50px
  font-size: 20px
  cursor: pointer
  padding: [0, 50px]
  background: '#3498db'
  line-height: 50px
  text-align: center
  display: inline-block
  hover:
    color: '#f1c40f'
    background: '#9b59b6'

Result

Button

Notes

A Fancy Progression

There is only one thing left to do — make it fancy.

HTML

<!-- HTML -->
<div class="blue-fancy-button">
Button
</div>
Edit
ctr('.blue-fancy-hover', {
color: white
height: 50px
font-size: 20px
cursor: pointer
padding: 0 50px
background: #3498db
line-height: 50px
text-align: center
display: inline-block
hover: {
background: #9b59b6
transform: {
rotate: 360deg
scale: 2
}
animation: {
name: 'fancy'
count: infinite
delay: 0.5s
timeline: {
'50': {
color: #27ae60
}
}
}
}
})
.blue-fancy-hover:
color: white
height: 50px
font-size: 20px
cursor: pointer
padding: [0, 50px]
background: '#3498db'
line-height: 50px
text-align: center
display: inline-block
hover:
background: '#9b59b6'
transform:
rotate: 360deg
scale: 2
animation:
name: fancy
count: infinite
delay: 0.5s
timeline:
50:
color: '#27ae60'
.blue-fancy-hover {
color: #fff;
height: 50px;
cursor: pointer;
font-size: 20px;
padding: 0 50px;
line-height: 50px;
text-align: center;
background: #3498db;
display: inline-block;
}
.blue-fancy-hover:hover {
background: #9b59b6;
animation-delay: 0.5s;
animation-name: fancy;
animation-duration: 0.5s;
transition-delay: 0s, 0s;
animation-fill-mode: none;
animation-direction: normal;
animation-play-state: running;
transition-duration: 0.5s, 0.5s;
animation-iteration-count: infinite;
transform: rotate(360deg) scale(2);
transition-property: background, transform;
animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
.blue-fancy-hover:not(:hover) {
transition-delay: 0s, 0s;
transition-duration: 0.5s, 0.5s;
transition-property: background, transform;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
@keyframes fancy {
50% {
color: #27ae60;
}
}
ctr('.blue-fancy-hover', {
  color: white
  height: 50px
  font-size: 20px
  cursor: pointer
  padding: 0 50px
  background: #3498db
  line-height: 50px
  text-align: center
  display: inline-block
  hover: {
    background: #9b59b6
    transform: {
      rotate: 360deg
      scale: 2
    }
    animation: {
      name: 'fancy'
      count: infinite
      delay: 0.5s
      timeline: {
        '50': {
          color: #27ae60
        }
      }
    }
  }
})
.blue-fancy-hover {
  color: #fff;
  height: 50px;
  cursor: pointer;
  font-size: 20px;
  padding: 0 50px;
  line-height: 50px;
  text-align: center;
  background: #3498db;
  display: inline-block;
}
.blue-fancy-hover:hover {
  background: #9b59b6;
  animation-delay: 0.5s;
  animation-name: fancy;
  animation-duration: 0.5s;
  transition-delay: 0s, 0s;
  animation-fill-mode: none;
  animation-direction: normal;
  animation-play-state: running;
  transition-duration: 0.5s, 0.5s;
  animation-iteration-count: infinite;
  transform: rotate(360deg) scale(2);
  transition-property: background, transform;
  animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
  transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
.blue-fancy-hover:not(:hover) {
  transition-delay: 0s, 0s;
  transition-duration: 0.5s, 0.5s;
  transition-property: background, transform;
  transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1), cubic-bezier(0.42, 0, 0.58, 1);
}
@keyframes fancy {
  50% {
    color: #27ae60;
  }
}
.blue-fancy-hover:
  color: white
  height: 50px
  font-size: 20px
  cursor: pointer
  padding: [0, 50px]
  background: '#3498db'
  line-height: 50px
  text-align: center
  display: inline-block
  hover:
    background: '#9b59b6'
    transform:
      rotate: 360deg
      scale: 2
    animation:
      name: fancy
      count: infinite
      delay: 0.5s
      timeline:
        50:
          color: '#27ae60'

Result

Button

Notes

A Component Progression

Building upon the previous examples let’s combine the buttons into a button component of three buttons.

HTML

<!-- HTML -->
<div class="a-button-component">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
Edit
ctr('.a-button-component', {
height: 200px
background: alpha(#3498db, 0.85)
border-radius: 4px
grid: {
container: true
align: 'center'
column: '1/1'
}
components: {
'*': {
color: #ecf0f1
height: 100px
background: alpha(#2ecc71, 0.75)
position: relative
before: {
alignSelf: 'center'
font-size: responsive 1em 3em
}
hover: {
// hover.css preset -> docs: state/hover.css/
preset: 'pulseGrow'
}
grid: {
column: '1/3'
}
}
'.one': {
before: {
content: 'one'
}
}
'.two': {
before: {
content: 'two'
}
}
'.three': {
before: {
content: 'three'
}
}
}
})
.a-button-component:
height: 200px
background: 'alpha(#3498db, 0.85)'
border-radius: 4px
grid:
container: true
align: center
column: 1/1
components:
'*':
color: '#ecf0f1'
height: 100px
background: 'alpha(#2ecc71, 0.75)'
position: relative
before:
alignSelf: center
font-size: [responsive, 1em, 3em]
hover:
# hover.css preset -> docs: state/hover.css/
preset: pulseGrow
grid:
column: 1/3
.one:
before:
content: one
.two:
before:
content: two
.three:
before:
content: three
.a-button-component {
display: flex;
height: 200px;
flex: 0 0 auto;
border-radius: 4px;
align-items: center;
flex-flow: row wrap;
justify-content: center;
background: rgba(52,152,219,0.85);
width: calc(99.9% * 1 / 1 - (30px - 30px * 1 / 1));
}
.a-button-component > * {
height: 100px;
color: #ecf0f1;
flex: 0 0 auto;
position: relative;
background: rgba(46,204,113,0.75);
width: calc(99.9% * 1 / 3 - (30px - 30px * 1 / 3));
}
.a-button-component:last-child {
margin-right: 0;
}
.a-button-component:nth-child(1n) {
float: right;
margin-right: 0;
}
.a-button-component > *::before {
top: 50%;
left: 50%;
right: auto;
bottom: auto;
position: absolute;
transform: translate(-50%, -50%);
font-size: calc(1em + 2 * ((100vw - 25em) / 87.5));
}
.a-button-component > *:hover {
animation-delay: 0s;
display: inline-block;
vertical-align: middle;
animation-duration: 0.3s;
transform: translateZ(0);
animation-fill-mode: none;
backface-visibility: hidden;
animation-play-state: running;
animation-direction: alternate;
animation-name: hvr-pulse-grow;
animation-timing-function: linear;
-moz-osx-font-smoothing: grayscale;
animation-iteration-count: infinite;
box-shadow: 0 0 1px rgba(0,0,0,0);
}
.a-button-component > *:not(:hover) {
display: inline-block;
vertical-align: middle;
transform: translateZ(0);
backface-visibility: hidden;
-moz-osx-font-smoothing: grayscale;
box-shadow: 0 0 1px rgba(0,0,0,0);
}
.a-button-component > .one::before {
content: "one";
}
.a-button-component > .two::before {
content: "two";
}
.a-button-component > .three::before {
content: "three";
}
.a-button-component > *:nth-child(1n) {
margin-right: 30px;
}
.a-button-component > *:last-child {
margin-right: 0;
}
.a-button-component > *:nth-child(3n) {
float: right;
margin-right: 0;
}
@media only screen and (max-width: 400px) {
.a-button-component > *::before {
font-size: 1em;
}
}
@media only screen and (min-width: 1800px) {
.a-button-component > *::before {
font-size: 3em;
}
}
@keyframes hvr-pulse-grow {
to {
transform: scale(1.1);
}
}
ctr('.a-button-component', {
  height: 200px
  background: alpha(#3498db, 0.85)
  border-radius: 4px
  grid: {
    container: true
    align: 'center'
    column: '1/1'
  }
  components: {
    '*': {
      color: #ecf0f1
      height: 100px
      background: alpha(#2ecc71, 0.75)
      position: relative
      before: {
        alignSelf: 'center'
        font-size: responsive 1em 3em
      }
      hover: {
        // hover.css preset -> docs: state/hover.css/
        preset: 'pulseGrow'
      }
      grid: {
        column: '1/3'
      }
    }
    '.one': {
      before: {
        content: 'one'
      }
    }
    '.two': {
      before: {
        content: 'two'
      }
    }
    '.three': {
      before: {
        content: 'three'
      }
    }
  }
})
.a-button-component {
  display: flex;
  height: 200px;
  flex: 0 0 auto;
  border-radius: 4px;
  align-items: center;
  flex-flow: row wrap;
  justify-content: center;
  background: rgba(52,152,219,0.85);
  width: calc(99.9% * 1 / 1 - (30px - 30px * 1 / 1));
}
.a-button-component > * {
  height: 100px;
  color: #ecf0f1;
  flex: 0 0 auto;
  position: relative;
  background: rgba(46,204,113,0.75);
  width: calc(99.9% * 1 / 3 - (30px - 30px * 1 / 3));
}
.a-button-component:last-child {
  margin-right: 0;
}
.a-button-component:nth-child(1n) {
  float: right;
  margin-right: 0;
}
.a-button-component > *::before {
  top: 50%;
  left: 50%;
  right: auto;
  bottom: auto;
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: calc(1em + 2 * ((100vw - 25em) / 87.5));
}
.a-button-component > *:hover {
  animation-delay: 0s;
  display: inline-block;
  vertical-align: middle;
  animation-duration: 0.3s;
  transform: translateZ(0);
  animation-fill-mode: none;
  backface-visibility: hidden;
  animation-play-state: running;
  animation-direction: alternate;
  animation-name: hvr-pulse-grow;
  animation-timing-function: linear;
  -moz-osx-font-smoothing: grayscale;
  animation-iteration-count: infinite;
  box-shadow: 0 0 1px rgba(0,0,0,0);
}
.a-button-component > *:not(:hover) {
  display: inline-block;
  vertical-align: middle;
  transform: translateZ(0);
  backface-visibility: hidden;
  -moz-osx-font-smoothing: grayscale;
  box-shadow: 0 0 1px rgba(0,0,0,0);
}
.a-button-component > .one::before {
  content: "one";
}
.a-button-component > .two::before {
  content: "two";
}
.a-button-component > .three::before {
  content: "three";
}
.a-button-component > *:nth-child(1n) {
  margin-right: 30px;
}
.a-button-component > *:last-child {
  margin-right: 0;
}
.a-button-component > *:nth-child(3n) {
  float: right;
  margin-right: 0;
}
@media only screen and (max-width: 400px) {
  .a-button-component > *::before {
    font-size: 1em;
  }
}
@media only screen and (min-width: 1800px) {
  .a-button-component > *::before {
    font-size: 3em;
  }
}
@keyframes hvr-pulse-grow {
  to {
    transform: scale(1.1);
  }
}
.a-button-component:
  height: 200px
  background: 'alpha(#3498db, 0.85)'
  border-radius: 4px
  grid:
    container: true
    align: center
    column: 1/1
  components:
    '*':
      color: '#ecf0f1'
      height: 100px
      background: 'alpha(#2ecc71, 0.75)'
      position: relative
      before:
        alignSelf: center
        font-size: [responsive, 1em, 3em]
      hover:
        # hover.css preset -> docs: state/hover.css/
        preset: pulseGrow
      grid:
        column: 1/3
    .one:
      before:
        content: one
    .two:
      before:
        content: two
    .three:
      before:
        content: three

Result

Notes

Syntax

The Stylus syntax for ctr is best described as a hybrid between Javascript and YAML. If you’re unfamiliar with the basic data structure of an Object, it would be in your best interest to hit pause now and do a bit of learning. Nevertheless, before you go any further, there are two critically important syntax rules you must abide by in a Stylus ctr instance.

  1. Never use semicolons - ;: Forget about them, do not even think about them, they do nothing but cause sorrow, pain, and errors in the Stylus parser.
  2. Never use commas - ,: In Stylus there is no need to separate Object Literals like you do with Javascript.

The syntax may feel a bit weird at first, especially if you do a fair amount of Object play in Javascript. Even if you despise the syntax at first, I recommend you give Stylus an honest run before you decide to ditch it in favor for the YAML or Javascript implementation.

Invocation

In Stylus, there are two ways to invoke a ctr instance, declaratively or imperatively. Each achieves the same results, although, depending on your use-case or style preference, one may prove to be a bit more advantageous than the other.

// Stylus -> imperative method
<selecor#>
ctr({
<key>: <value>
})
// Stylus -> declarative method
ctr('<selecor#>', {
<key>: <value>
})
/*CSS -> Same output for both*/
<selecor#> {
<key>: <value>
}

Notes

Declarative Invocation

The declarative invocation is the recommended invocation method. The syntax is clear and concise, plus it forces conformity to the ctr paradigm. However, more importantly, it offers complete control to handle any CSS situation under the sun, and the Stylus API mimics that of the Javascript API.

Edit
ctr('.aClass', {
width: 200px
height: 200px
})
ctr('#aId', {
width: 200px
height: 200px
background: red
})
ctr('#aId span', {
width: 200px
font-size: 1em
})
.aClass:
width: 200px
height: 200px
'#aId':
width: 200px
height: 200px
background: red
'#aId span':
width: 200px
font-size: 1em
.aClass {
width: 200px;
height: 200px;
}
#aId {
width: 200px;
height: 200px;
background: #f00;
}
#aId span {
width: 200px;
font-size: 1em;
}
ctr('.aClass', {
  width: 200px
  height: 200px
})

ctr('#aId', {
  width: 200px
  height: 200px
  background: red
})

ctr('#aId span', {
  width: 200px
  font-size: 1em
})
.aClass {
  width: 200px;
  height: 200px;
}
#aId {
  width: 200px;
  height: 200px;
  background: #f00;
}
#aId span {
  width: 200px;
  font-size: 1em;
}
.aClass:
  width: 200px
  height: 200px
'#aId':
  width: 200px
  height: 200px
  background: red
'#aId span':
  width: 200px
  font-size: 1em

Notes

Imperative Invocation

The imperative invocation shines if you don’t want to drink all the ctr kool-aid or only intend to use it for specific features. For example, let’s say you only want to use ctr for animations, the imperative invocation method tailors to this use case because you can use it inline with the rest of your Stylus styles.

Edit
.aClass
ctr({
width: 200px
height: 200px
})
#aId
ctr({
width: 200px
height: 200px
background: red
})
span
ctr({
width: 200px
font-size: 1em
})
.aClass {
width: 200px;
height: 200px;
}
#aId {
width: 200px;
height: 200px;
background: #f00;
}
#aId span {
width: 200px;
font-size: 1em;
}
.aClass
  ctr({
    width: 200px
    height: 200px
  })

#aId
  ctr({
    width: 200px
    height: 200px
    background: red
  })
  span
    ctr({
      width: 200px
      font-size: 1em
    })
.aClass {
  width: 200px;
  height: 200px;
}
#aId {
  width: 200px;
  height: 200px;
  background: #f00;
}
#aId span {
  width: 200px;
  font-size: 1em;
}

Notes