Info¶
To better encapsulate local and global variables, ctr
has a custom variable schema. The idea is quite similar to variables in a CSS preprocessor, just tailored to fit the ctr
paradigm. Unlike preprocessor variables, ctr
variables are built with a logical variable declaration structure that adds safety precautions. To better understand why ctr
variables are my best friend, and will soon become yours too, let us look at the problem they solve.
The Problem¶
It’s commonplace to have a centralized variable file in a Stylus, Sass, Less, or PostCss project. This file is typically comprised of mission-critical variables such as primary colors, font styles, and everything else in between. However, there is a big catch: all the variable declarations are global. This is fine for a small project, but as a project grows it becomes increasingly unruly due to the inherent limitations of a single namespace. From cryptic variable names to the ever-looming knowledge that someone can come along at any point and overwrite a variable unknowingly, global variables are simply dangerous. Unless you live on the edge and BASE jump while smoking cigarettes on the weekends, I would advise you shy away.
The Solution¶
The solution comes in the form of a specialized namespace to facilitate both local and global variables. Like everything in ctr
, the variable namespace is an Object whose properties and values are defined in key-value pair fashion to go hand in hand with the data structure of ctr
. Defined key-value pair variable values can then be used in any ctr
instance or class.
The global variable namespace is a single namespace which is shared among all ctr
instances and classes. It’s comparable to a centralized CSS preprocessor variable file. On the other hand, the local variable namespace is a private namespace that can only be used within its defined ctr
instance or class.
The global and local variable namespace allows you to create next-level DRY styles while leveraging the benefits of variable encapsulation. However, the best part about ctr
variables is the default level of safety they offer by not allowing you to overwrite previously defined variables.
Syntax¶
Description: A ctr
variable is defined as a local or global variable that can be used in a ctr
instance or class.
ctr('<#selector>', {
// local variables
$$: {
one: <value:one>
two: <value:two>
obj: {
three: <value:three>
}
}
<property:one>: '$one$'
<property:two>: '$two$'
<property:three>: '$[obj.three]$'
})
<#selector>:
# local variables
$$:
one: <value:one>
two: <value:two>
obj:
three: <value:three>
<property:one>: $one$
<property:two>: $two$
<property:three>: $[obj.three]$
<#selector> {
<property:one>: <value:one>;
<property:two>: <value:two>;
<property:three>: <value:three>;
}
// global variables
ctrSetVariable({
one: <value:one>
two: <value:two>
obj: {
three: <value:three>
}
})
ctr('<#selector>', {
<property:one>: '$one$'
<property:two>: '$two$'
<property:three>: '$[obj.three]$'
})
# global variables
ctr:::SetVariable:
one: <value:one>
two: <value:two>
obj:
three: <value:three>
<#selector>:
<property:one>: $one$
<property:two>: $two$
<property:three>: $[obj.three]$
<#selector> {
<property:one>: <value:one>;
<property:two>: <value:two>;
<property:three>: <value:three>;
}
Notes
- Variable lookup is performed through lodash’s
_.get
Function using dot notation:$<path>.<to>.<var>$
- Alternative syntax:
$[<path>.<to>.<var>]$
- If and when to use the alternative syntax is completely up to you, but my rule of thumb is to use it with dot notation or when two variables are butting up against one another
- Alternative syntax:
- Local and global variables can be used together and have a symbiotic relationship like electrons in a covalent bond in which
ctr
is the atom
Local¶
Description: Local variables are defined in the root of the ctr
instance or class through an Object key of $$
. Variables in the $$
Object are declared in key-value pair fashion and their value can be a String, Object, List, or a Literal. To reference a local variable, the absolute variable path relative to the $$
Object is specified as a String wrapped in dollar signs as such: '$<path>.<to>.<varible>$'
.
ctr('.test', {
$$: {
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
}
width: '$width$'
font-size: '$fontSize$'
component-span: {
color: '$color$'
// alternative syntax
height: '$[pretty-cool]$'
}
})
.test:
$$:
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
width: $width$
font-size: $fontSize$
component-span:
color: $color$
# alternative syntax
height: $[pretty-cool]$
.test {
width: 200px;
font-size: 1em;
}
.test > span {
color: #f00;
height: 300px;
}
ctr('.test', {
$$: {
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
}
width: '$width$'
font-size: '$fontSize$'
component-span: {
color: '$color$'
// alternative syntax
height: '$[pretty-cool]$'
}
})
.test {
width: 200px;
font-size: 1em;
}
.test > span {
color: #f00;
height: 300px;
}
.test:
$$:
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
width: $width$
font-size: $fontSize$
component-span:
color: $color$
# alternative syntax
height: $[pretty-cool]$
Notes
- Local variables always supersede global variables
- On the surface level, local variables keep your code extremely DRY, but where they really shine is within a
ctr
class
instance
Global¶
Description: Global variables can be defined through a ctrSetVariable
invocation or in the .ctrrc.yml
file. Variables are declared in key-value pair fashion and their value can be a String, Object, List, or a Literal. To reference a global variable, the absolute variable path is specified as a String wrapped in dollar signs as such: '$<path>.<to>.<varible>$'
.
ctrSetVariable({
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
})
ctr('.test', {
width: '$width$'
font-size: '$fontSize$'
component-span: {
color: '$color$'
// alternative syntax
height: '$[pretty-cool]$'
}
})
ctr:::setVariable:
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
.test:
width: $width$
font-size: $fontSize$
component-span:
color: $color$
# alternative syntax
height: $[pretty-cool]$
.test {
width: 200px;
font-size: 1em;
}
.test > span {
color: #f00;
height: 300px;
}
ctrSetVariable({
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
})
ctr('.test', {
width: '$width$'
font-size: '$fontSize$'
component-span: {
color: '$color$'
// alternative syntax
height: '$[pretty-cool]$'
}
})
.test {
width: 200px;
font-size: 1em;
}
.test > span {
color: #f00;
height: 300px;
}
ctr:::setVariable:
width: 200px
color: red
fontSize: 1em
pretty-cool: 300px
.test:
width: $width$
font-size: $fontSize$
component-span:
color: $color$
# alternative syntax
height: $[pretty-cool]$
Notes
- Local variables always supersede global variables
- Global variables share a single immutable Map and new values are added through mergeDeep
- Like all global variables in any language, they carry inherent baggage. Although, if you accidentally try to overwrite a variable it will throw you a warning message which you can override if you wish
- The yaml syntax is
ctr:::setVar:
and it works just like StylusctrSetVar
iables are declared in key-value pair fashion and their value can be a String, Object, List, or a Literal. To reference a global variable the absolute variable path is specified as a String wrapped in dollar signs as such:'$<path>.<to>.<varible>$'
.
List¶
Description: A List value can be used with local and global variables.
ctr('.test', {
$$: {
keyList: '.easy' '#as' '.oneTwoThree'
}
width: 200px
component: {
key: '$keyList$'
font-size: 1em
}
})
.test:
$$:
keyList: [.easy, '#as', .oneTwoThree]
width: 200px
component:
key: $keyList$
font-size: 1em
.test {
width: 200px;
}
.test > .easy {
font-size: 1em;
}
.test > #as {
font-size: 1em;
}
.test > .oneTwoThree {
font-size: 1em;
}
ctr('.test', {
$$: {
keyList: '.easy' '#as' '.oneTwoThree'
}
width: 200px
component: {
key: '$keyList$'
font-size: 1em
}
})
.test {
width: 200px;
}
.test > .easy {
font-size: 1em;
}
.test > #as {
font-size: 1em;
}
.test > .oneTwoThree {
font-size: 1em;
}
.test:
$$:
keyList: [.easy, '#as', .oneTwoThree]
width: 200px
component:
key: $keyList$
font-size: 1em
Literal¶
Description: A Literal value can be used with local and global variables.
ctr('.test', {
$$: {
font-size: 12px
}
width: 200px
font-size: '$font-size$'
})
.test:
$$:
font-size: 12px
width: 200px
font-size: $font-size$
.test {
width: 200px;
font-size: 12px;
}
ctr('.test', {
$$: {
font-size: 12px
}
width: 200px
font-size: '$font-size$'
})
.test {
width: 200px;
font-size: 12px;
}
.test:
$$:
font-size: 12px
width: 200px
font-size: $font-size$
Object¶
Description: An Object value can be used with local and global variables.
ctr('.test', {
$$: {
myHoverState: {
on: {
opacity: 1
}
non: {
opacity: 0.5
}
}
}
width: 200px
hover: '$myHoverState$'
})
.test:
$$:
myHoverState:
on:
opacity: 1
non:
opacity: 0.5
width: 200px
hover: $myHoverState$
.test {
width: 200px;
}
.test:hover {
opacity: 1;
transition-delay: 0s;
transition-duration: 0.5s;
transition-property: opacity;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
}
.test:not(:hover) {
opacity: 0.5;
transition-delay: 0s;
transition-duration: 0.5s;
transition-property: opacity;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
}
ctr('.test', {
$$: {
myHoverState: {
on: {
opacity: 1
}
non: {
opacity: 0.5
}
}
}
width: 200px
hover: '$myHoverState$'
})
.test {
width: 200px;
}
.test:hover {
opacity: 1;
transition-delay: 0s;
transition-duration: 0.5s;
transition-property: opacity;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
}
.test:not(:hover) {
opacity: 0.5;
transition-delay: 0s;
transition-duration: 0.5s;
transition-property: opacity;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
}
.test:
$$:
myHoverState:
on:
opacity: 1
non:
opacity: 0.5
width: 200px
hover: $myHoverState$
String¶
Description: A String value can be used with local and global variables.
ctr('.test', {
$$: {
content: 'hacker news'
}
width: 200px
before: {
content: '$content$'
}
})
.test:
$$:
content: [hacker, news]
width: 200px
before:
content: $content$
.test {
width: 200px;
}
.test::before {
content: "hacker news";
}
ctr('.test', {
$$: {
content: 'hacker news'
}
width: 200px
before: {
content: '$content$'
}
})
.test {
width: 200px;
}
.test::before {
content: "hacker news";
}
.test:
$$:
content: [hacker, news]
width: 200px
before:
content: $content$
Multiple String¶
Description: Multiple local and global ctr
variables can be used together in a String.
ctr('.test', {
$$: {
hLength: 1px
vLength: 2px
blur: 3px
spread: 4px
background: red
}
width: 200px
box-shadow: '$hLength$ $vLength$ $blur$ $spread$ $background$'
})
.test:
$$:
hLength: 1px
vLength: 2px
blur: 3px
spread: 4px
background: red
width: 200px
box-shadow: [$hLength$, $vLength$, $blur$, $spread$, $background$]
.test {
width: 200px;
box-shadow: 1px 2px 3px 4px #f00;
}
ctr('.test', {
$$: {
hLength: 1px
vLength: 2px
blur: 3px
spread: 4px
background: red
}
width: 200px
box-shadow: '$hLength$ $vLength$ $blur$ $spread$ $background$'
})
.test {
width: 200px;
box-shadow: 1px 2px 3px 4px #f00;
}
.test:
$$:
hLength: 1px
vLength: 2px
blur: 3px
spread: 4px
background: red
width: 200px
box-shadow: [$hLength$, $vLength$, $blur$, $spread$, $background$]
Property Variable¶
Description: Object key properties can be replaced with local and global variables.
ctr('.test', {
$$: {
myProp: 'color'
}
width: 200px
$myProp$: red
})
.test:
$$:
myProp: color
width: 200px
$myProp$: red
.test {
color: #f00;
width: 200px;
}
ctr('.test', {
$$: {
myProp: 'color'
}
width: 200px
$myProp$: red
})
.test {
color: #f00;
width: 200px;
}
.test:
$$:
myProp: color
width: 200px
$myProp$: red
Notes
- Dreams come true when you combine property replacement and
ctr
classes - By default, the property variable option is
false
(i.e. off) in Javascript because, well, it’s Javascript - Global option property:
global.propertyVariable
Sibling Reference¶
Description: Local variables can reference previously declared siblings.
ctr('.test', {
$$: {
size: 12
type: 'rem'
font-size: 'calc($[size]$$[type]$ * ((100vw - 25rem) / 87.5))'
}
width: 200px
font-size: '$font-size$'
})
.test:
$$:
size: 12
type: rem
font-size: calc($[size]$$[type]$ * ((100vw - 25rem) / 87.5))
width: 200px
font-size: $font-size$
.test {
width: 200px;
font-size: calc(12rem * ((100vw - 25rem) / 87.5));
}
ctr('.test', {
$$: {
size: 12
type: 'rem'
font-size: 'calc($[size]$$[type]$ * ((100vw - 25rem) / 87.5))'
}
width: 200px
font-size: '$font-size$'
})
.test {
width: 200px;
font-size: calc(12rem * ((100vw - 25rem) / 87.5));
}
.test:
$$:
size: 12
type: rem
font-size: calc($[size]$$[type]$ * ((100vw - 25rem) / 87.5))
width: 200px
font-size: $font-size$
Notes
- The ability to reference previous sibling values is dependant on property order, which is a convoluted topic, but here is what you need to know:
- Object order is not guaranteed, but in my opinion you can make the reasonable assumption that it is
- I have never run into any issues, although, if particles can walk through walls, an incorrect Object order is bound to happen to someone, somewhere, at some point in time
- If a reference value is not found,
ctr
will throw you a warning message which acts as a safety catch
- Never use integer keys since they bubble to the top in ascending order
- If you need to perform some advance wizardry, just use Javascript
- Object order is not guaranteed, but in my opinion you can make the reasonable assumption that it is
- Global option property:
global.variableUpdate
Var Not Found¶
Description: If a local or global variable is not found, ctr
will throw an error and return "$var-not-found$"
.
ctr('.test', {
$$: {
height: 200px
}
width: 200px
// wrong var name
height: '$not-height$'
})
.test:
$$:
height: 200px
width: 200px
# wrong var name
height: $not-height$
.test {
width: 200px;
height: "$var-not-found$";
}
ctr('.test', {
$$: {
height: 200px
}
width: 200px
// wrong var name
height: '$not-height$'
})
.test {
width: 200px;
height: "$var-not-found$";
}
.test:
$$:
height: 200px
width: 200px
# wrong var name
height: $not-height$