Quick start guide
This is the quickest way to see the way style is written in this framework, without explanation of why it is so and what are the advantages of such an approach.
We offer 3 ways to explain our methodology and framework:
Quick start guide (current document) -
takes 15 minutes
- The quickest way to see the way style is written in this framework, without explanation of why it is so and what are the advantages of such an approach.Tutorial -
takes 1 hour
- A basic overview that is organized so that it can be read from start to end and so that the reader understands the fundamental principles and basic ways of using the framework.Documentation -
takes hours
- Comprehensive documentation of everything, organized by mixins and framework entities, so you can quickly find exactly what you want to learn more about.
Choose the one that fits your current needs best.
Intro
SPOT CSS methodology and framework is all about architecture!
The main objective was to design rules that will unify the resulting code into a single possible form, no matter who writes it.
Fasten your seat belt, this is going to be a fast ride!
Why SASS syntax (not SCSS)?
Main reason - because you don't need to write @include
but only +
. And in this framework, you will do it very often. So this way with SASS syntax it's more simple and more readable.
- SCSS
@include lorem-ipsum {
property: value;
}
- SASS
+lorem-ipsum
property: value
Installation
Install spotcss via npm in your project:
- Terminal
$ npm i --save-dev spotcss
Import spotcss framework in your style:
- SASS
- SCSS
@import "~spotcss"
@import "~spotcss";
Component
Component is a very base entity in this component-based methodology and framework. It can be any simple/single element, or any block of the website (structure of elements).
- SASS
- SCSS
+component('button')
display: inline-block
@include component('button') {
display: inline-block;
}
button {
display: inline-block;
}
Element
A component can be composed by one element but sometimes by several elements, and the following mixin +element help us define them.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
display: inline-block
+element('.icon')
vertical-align: middle
+element('> img')
width: 100%
+element('.text')
font-size: 1.2em
@include mode('draft'); // will be explained later
@include component('button') {
display: inline-block;
@include element('.icon') {
vertical-align: middle;
@include element('> img') {
width: 100%;
}
}
@include element('.text') {
font-size: 1.2em;
}
}
- CSS
- HTML
button {
display: inline-block;
}
button .icon {
vertical-align: middle;
}
button .icon > img {
width: 100%;
}
button .text {
font-size: 1.2em;
}
<button>
<span class="icon">
<img src="{{ iconSrc }}" />
</span>
<span class="text">
{{ buttonText }}
</span>
</button>
Pseudo-element
Even +pseudo-element has a separate mixin and works just like the +element.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
display: inline-block
+pseudo-element('before')
content: ''
@include mode('draft'); // will be explained later
@include component('button') {
display: inline-block;
@include pseudo-element('before') {
content: '';
}
}
button {
display: inline-block;
}
button::before {
content: '';
}
Mutations
When component has more different appearances, we call it a mutation.
The difference can be on the component root element or components sub-elements.
If element has more appearances, it has a +default appearance and other mutations.
Default
When component has more mutations, the "default" mutation must be declared and should go first.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
@include mode('draft'); // will be explained later
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include variant('.primary') {
background-color: green;
}
}
button {
display: inline-block;
background-color: grey;
}
button.primary {
background-color: green;
}
If no mutations, no default is needed:
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
display: inline-block
background-color: grey
@include mode('draft'); // will be explained later
@include component('button') {
display: inline-block;
background-color: grey;
}
button {
display: inline-block;
background-color: grey;
}
Variant
Variant is the basic type of the mutation. It defines other than default appearance of the component.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
@include mode('draft'); // will be explained later
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include variant('.primary') {
background-color: green;
}
}
button {
display: inline-block;
background-color: grey;
}
button.primary {
background-color: green;
}
The same principle applies to sub-elements:
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
+element('.text')
+default
font-weight: 1.2em
+variant('.primary')
font-weight: 1.6em
@include mode('draft'); // will be explained later
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include variant('.primary') {
background-color: green;
}
@include element('.text') {
@include default {
font-weight: 1.2em;
}
@include variant('.primary') {
font-weight: 1.6em;
}
}
}
button {
display: inline-block;
background-color: grey;
}
button.primary {
background-color: green;
}
button .text {
font-weight: 1.2em;
}
button.primary .text {
font-weight: 1.6em;
}
Important: note that the class/variant .primary
was applied to the root element selector of the component in both cases (button.primary .text
and not as button .text.primary
).
State
Mixin +state works exactly same as mixin +variant. The only difference is in semantics. But it can be combined together too.
You can find out more about difference between state and variant here.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
+default
display: inline-block
background-color: grey
+state(':hover')
background-color: yellow
+variant('.primary')
+default
background-color: green
+state(':hover')
background-color: white
@include mode('draft'); // will be explained later
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include state(':hover') {
background-color: yellow;
}
@include variant('.primary') {
@include default {
background-color: green;
}
@include state(':hover') {
background-color: white;
}
}
}
button {
display: inline-block;
background-color: grey;
}
button:hover {
background-color: yellow;
}
button.primary {
background-color: green;
}
button.primary:hover {
background-color: white;
}
Context
This one is VERY IMPORTANT and most often completely misunderstood and unrealized the substance and impact of this approach.
It is not even possible to cover the whole idea in this super quick fly-through but be sure not to underestimate it so please study this later: Context in depth.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
+default
font-size: 1.5em
+context('.product')
font-size: 1.25em
@include mode('draft'); // will be explained later
@include component('button') {
@include default {
font-size: 1.5em;
}
@include context('.product') {
font-size: 1.25em;
}
}
button {
font-size: 1.5em;
}
.product button {
font-size: 1.25em;
}
As you can see it adds the context before the component selector. But this is only a very shallow explanation.
Responsive
Mixin +responsive works similar to mixin +context. The main difference is in semantics and that you can use media queries.
- SASS
- SCSS
+mode('draft')
+component('button')
+default
display: inline-block
background-color: grey
+responsive('html.mobile-portrait')
display: block
+responsive('@media (max-width: 360px)')
display: flex
@include mode('draft');
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include responsive('html.mobile-portrait') {
display: block;
}
@include responsive('@media (max-width: 360px)') {
display: flex;
}
}
button {
display: inline-block;
background-color: grey;
}
html.mobile-portrait button {
display: block;
}
@media (max-width: 360px) {
button {
display: flex;
}
}
Specificity
In SPOT CSS framework the CSS selector specificity problem is not a problem anymore.
You can increase element level
, class level
or id level
specificity
or you can say what you want to override directly.
- SASS
- SCSS
+mode('draft') // will be explained later
+component('button')
+default
display: inline-block
+add-class-specificity(1)
background-color: grey
+variant('.primary')
+override('.btn[type="submit"].primary')
background-color: green
color: black
@include mode('draft'); // will be explained later
@include component('button') {
@include default {
display: inline-block;
@include add-class-specificity(1) {
background-color: grey;
}
}
@include variant('.primary') {
@include override('.btn[type="submit"].primary') {
background-color: green;
color: black;
}
}
}
button {
display: inline-block;
}
button:not(._) {
background-color: grey;
}
button.primary:not(._):not(._) {
background-color: green;
color: black;
}
Mode
In all examples above we were using +mode('draft')
. This is a special mode for prototyping.
If you want to write something really fast a try if it works then activate this mode.
In draft mode you don't need to do/write certain things. The code can be shorter. That's the reason why it's used in all examples until now. You don't need to write/use:
- element separators
- section register
- can't use aliases, because of absence register section
- there is no need to use the +default mixin
- some strict checks for structure are not evaluated and therefore no warnings when parsing SASS
Mode has to be set before (each) component because it's applied only for the following component.
Default mode is strict
and it doesn't need to be declared (because it's default).
- SASS
- SCSS
+mode('draft') // this is explained right here and right now
...
// +mode('strict') // you don't need to declare strict mode because it is a default
@include mode('draft'); // this is explained right here and right now
...
//@include mode('strict'); // you don't need to declare strict mode because it's default
The following examples would be without the mode draft which means the strict mode.
Separator
Each element must be preceded by a separator. It's a mixin of underscores (as many as you want) and the parser will yell at you with warning if you forget it (in strict mode).
+______ // this works
+______________ // this is ok
+________________________ // this is fine too
+____________________________________ // the length is up to you
It must be used before every +element and +pseudo-element.
It is used for visual perception where the start and the end of the css code of every element is and it proved to be a very useful (mandatory) mixin.
- SASS
+component('button')
...
+____________________________
+element('.icon')
vertical-align: middle
+____________________________
+pseudo-element('after')
width: 100%
+____________________________
+element('.text')
font-size: 1.2em
Why it's useful and why it's worth it you can read in section Single place of element below.
But as you can see below it helps to fast scan the code and to orient in component sub-elements:
+mode('draft')
+component('button.btn')
display: inline-block
color: #3432A1
line-height: 1.5em
+_________________________
+element('.icon')
+default
display: inline-block
color: #3432A1
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
+default
font-size: 1.2em
display: inline-block
+state(':hover')
vertical-align: middle
margin: 0.2em
+_________________________
+element('img')
+default
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+_________________________
+element('.text')
+default
display: inline-block
color: #3432A1
line-height: 1.5em
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+mode('draft')
+component('button.btn')
display: inline-block
color: #3432A1
line-height: 1.5em
+_________________________
+element('.icon')
+default
display: inline-block
color: #3432A1
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
+default
font-size: 1.2em
display: inline-block
+state(':hover')
vertical-align: middle
margin: 0.2em
+_________________________
+element('img')
+default
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+_________________________
+element('.text')
+default
display: inline-block
color: #3432A1
line-height: 1.5em
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+mode('draft')
+component('button.btn')
display: inline-block
color: #3432A1
line-height: 1.5em
+_________________________
+element('.icon')
+default
display: inline-block
color: #3432A1
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
+default
font-size: 1.2em
display: inline-block
+state(':hover')
vertical-align: middle
margin: 0.2em
+_________________________
+element('img')
+default
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+_________________________
+element('.text')
+default
display: inline-block
color: #3432A1
line-height: 1.5em
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
The above example shows one very important thing - if you take any highlighted element block there is no other CSS code addressing that element outside of this single contiguous block.
Register
In strict mode you have to register every +element and +pseudo-element and also every mutation of the component in special section "register" right after the component declaration.
- SASS
+component('button')
+register
+variant('.primary')
+element('.icon')
+element('img')
+default
// code
+variant('.primary')
// code
+____________________
+element('.icon')
// code
+____________________
+element('img')
+default
// code
+variant('.primary')
// code
- CSS
button {
/* code */
}
button.primary {
/* code */
}
button .icon {
/* code */
}
button img {
/* code */
}
button.primary img {
/* code */
}
The register section allows you (among other things) to declare a mutation for sub-element of the component (and not only for the root element) as you can see in the following example:
- SASS
+component('button')
+register
+variant('.primary')
+element('.icon')
+element('img')
+state('.loading')
+default
// code
+variant('.primary')
// code
+____________________
+element('.icon')
// code
+____________________
+element('img')
+default
// code
+state('.loading')
// code
- CSS
button {
/* code */
}
button.primary {
/* code */
}
button .icon {
/* code */
}
button img {
/* code */
}
button img.loading {
/* code */
}
Aliases
Another benefit of using register mixin is the ability to declare aliases.
Alias must be enclosed in curly brackets: {alias name}
and it have to be unique in component.
- SASS
+component('{button}', 'button.btn')
+register
+state('{focused}', ':focus', '.focused')
+variant('{highlighted}', '.go01-flushed')
+pseudo-element('{caret}', 'after')
+default
// code
+state('{focused}')
// code
+__________________________
+pseudo-element('{caret}')
+default
// code
+variant('{highlighted}')
// code
...
Only-for
There is no need to register tree-structural pseudo-classes. Just use the mixin +only-for.
- SASS
+______________________________
+element('p')
+default
margin: 10px 0
+only-for(':first-child')
margin: 0
p {
margin: 10px 0;
}
p:first-child {
margin: 0
}
Or you can use the mixin +only-for for reusing the generic code.
Example of grouped mutations:
- SASS
+register
+variant('.lorem')
+variant('.ipsum')
...
+variant('.lorem', '.ipsum')
+default
overflow: hidden
margin: 1em
+only-for('.lorem')
display: block
+only-for('.ipsum')
display: flex
.lorem, .ipsum {
overflow: hidden;
margin: 1em;
}
.lorem {
display: block;
}
.ipsum {
display: flex;
}
Example of exclusive mutations:
- SASS
+register
+state('{collapsible}', '.expanded', '.collapsed')
...
+state('{collapsible}')
+default
overflow: hidden
margin: 1em
+only-for('.expanded')
display: block
+only-for('.collapsed')
display: flex
.expanded, .collapsed {
overflow: hidden;
margin: 1em;
}
.expanded {
display: block;
}
.collapsed {
display: flex;
}
Example of elements:
- SASS
+____________________________________
+element('th', 'td')
+default
overflow: hidden
margin: 1em
+only-for('th')
color: white
+only-for('td')
color: grey
th, td {
overflow: hidden;
margin: 1em;
}
th {
color: white;
}
td {
color: grey;
}
Example of pseudo-elements:
- SASS
+____________________________________
+pseudo-element('before', 'after')
+default
overflow: hidden
margin: 1em
+only-for('::before')
color: white
+only-for('::after')
color: grey
my-button::before, my-button::after {
overflow: hidden;
margin: 1em;
}
my-button::before {
color: white;
}
my-button::after {
color: grey;
}
Single place of element
The name SPOT CSS as the abbreviation of "Single Place of Truth". That's the core idea behind this methodology: "Everything in a single spot".
It applies to all entities - component, element and mutation too:
- Component
+mode('draft')
+component('button.btn')
display: inline-block
color: #3432A1
line-height: 1.5em
+_________________________
+element('.icon')
+default
display: inline-block
color: #3432A1
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
+default
font-size: 1.2em
display: inline-block
+state(':hover')
vertical-align: middle
margin: 0.2em
+_________________________
+element('img')
+default
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+_________________________
+element('.text')
+default
display: inline-block
color: #3432A1
line-height: 1.5em
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
- Element
+mode('draft')
+component('button.btn')
display: inline-block
color: #3432A1
line-height: 1.5em
+_________________________
+element('.icon')
+default
display: inline-block
color: #3432A1
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
+default
font-size: 1.2em
display: inline-block
+state(':hover')
vertical-align: middle
margin: 0.2em
+_________________________
+element('img')
+default
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+_________________________
+element('.text')
+default
display: inline-block
color: #3432A1
line-height: 1.5em
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
- Mutation
+mode('draft')
+component('button.btn')
display: inline-block
color: #3432A1
line-height: 1.5em
+_________________________
+element('.icon')
+default
display: inline-block
color: #3432A1
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
+default
font-size: 1.2em
display: inline-block
+state(':hover')
vertical-align: middle
margin: 0.2em
+_________________________
+element('img')
+default
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
+_________________________
+element('.text')
+default
display: inline-block
color: #3432A1
line-height: 1.5em
+state(':hover')
vertical-align: middle
margin: 0.2em
+state('.pressed')
width: 100%
height: auto
+variant('.primary')
font-size: 1.2em
display: inline-block
There are few axioms that every SPOT CSS code must comply:
any specific element on the page is addressed by one component only (or none if not styled)
style of every component is written in one contiguous block (and there is no other place of additional style for this component) - as you can see in the first column of the example above.
style of every sub-element of component is written in one contiguous block (and there is no other place of additional style for this sub-element) - as you can see in the second column of the example above.
style of every mutation in any sub-element or component is written in one contiguous block (and there is an exception for this case but no other place of additional style out of this sub-element) - as you can see in the third column of the example above.
This topic "Single place of element" is very much intertwined with the context(s) topic. But that is beyond the scope of this quick start guide.
Scratching the surface...
This was just scratching the surface to make you a picture about the SPOT CSS framework. But there is also a deeper theory behind this methodology.
If you want to know more, it's time to check out the tutorial (a deeper dive) or the full documentation.