REST
Intro
The problem
Why is CSS a pain?
Because it's global
Each declaration can affect anything in the document.Changes come in from anywhere
Each declaration can affect anything from any line and any file.Rules can affect unwanted targets
Each declaration can affect more than the intended target.It can be done in infinite ways
The points above make any task doable in a millions ways.
Solution
The goals of this methodology:
develop with simplicity, clarity and definiteness
Avoid unpredictable dependencies and scattered information in many files.maintain with confidence without side-effects
Watch out for unwanted outcomes, avoid uncertain results.scaling project and team
Do not slow down as the project grows. Keep developers substitutable.prevent resignation from CSS entropy
Do not let your project fall into chaos where the only solution is to start over.
Main concept
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.
The solution is to have all rules for specific element in one and only SINGLE PLACE.
Hence the name SPOT CSS as the abbreviation of "Single Place of Truth".
3 Rules
There are only 3 simple rules to follow. And SASS compiler will let you know if you break them.
#1 One entity in one place
The most important rule is ONE ENTITY IN ONE PLACE, hence the name "Single Place Of Truth".
This applies to any entity, such as a component, element, or mutation.
You will find any entity always in one contiguous block.
#2 Fixed semantic order
The second rule is FIXED SEMANTIC ORDER of all entities:
A component contains:
- register - the very first...
- default -
- state
- variant
- responsive
- context
- pseudo-element - repeat entities with order 2. - 6.
- element - repeat entities with order 2. - 6.
When this prescripted order is not followed, SASS parser will let you know that you break the 2nd rule.
This rule brings the ability to find (or not to find) everything with absolute certainty. Because even if you can't find something (on its prescripted place), it means it doesn't exist.
#3 Follow HTML structure
The third and last rule is FOLLOW HTML STRUCTURE to find everything very quickly.
- HTML
<button>
<span class="icon">
<img src="{{ buttonImage }}" />
</span>
<span class="text">
button name
</span>
</button>
- SASS
- SCSS
- SASS
- SCSS
+mode('draft')
+component('button')
display: inline-block
+____________________________
+element('.icon')
vertical-align: middle
+____________________________
+element('img')
width: 100%
+____________________________
+element('.text')
font-size: 1.2em
@include mode('draft');
@include component('button') {
display: inline-block;
@include ____________________;
@include element('.icon') {
vertical-align: middle;
@include _____________________;
@include element('img') {
width: 100%;
}
}
@include ____________________;
@include +element('.text') {
font-size: 1.2em;
}
}
+mode('strict')
+component('button')
+register
+element('.icon')
+element('img')
+element('.text')
display: inline-block
+____________________________
+element('.icon')
vertical-align: middle
+____________________________
+element('img')
width: 100%
+____________________________
+element('.text')
font-size: 1.2em
@include mode('strict');
@include component('button') {
@include register {
@include element('.icon') {
@include element('img');
}
@include element('.text');
}
display: inline-block;
@include ____________________;
@include element('.icon') {
vertical-align: middle;
@include _____________________;
@include element('img') {
width: 100%;
}
}
@include ____________________;
@include +element('.text') {
font-size: 1.2em;
}
}
If your component has sub elements, a SASS/SCSS code have to reflect exact same element structure.
Why SASS syntax (not SCSS)?
Because you don't need to write @include
but only +
. And in this framework, you will do it very often.
It's more simple and more readable.
Of course there are other differences: tabs instead curly brackets {
}
and no semicolons ;
.
But the main reason is omiting the @include
keyword.
- SCSS
@include lorem() {
property: value;
@include ipsum('dolor') {
property: value;
@include sit('amet') {
@include consectetur {
property: value;
}
}
}
}
- SASS
+lorem()
property: value
+ipsum('dolor')
property: value
+sit('amet')
+consectetur
property: value
But when you don't like SASS syntax (or just not used to), you can easily write SCSS. Therefore, all examples are also given in this syntax. It's a matter of preference (but also habit).
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
- SASS
- SCSS
+component('button')
display: inline-block
@include component('button') {
display: inline-block;
}
button {
display: inline-block;
}
Mutations
When one entity has more appearances, we call it a MUTATION.
If element has only one appearance, we call it element without mutations. If element has more appearances, it has default appearance and other mutations.
Default
- SASS
- SCSS
- SASS
- SCSS
+mode('draft')
+component('button')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
@include mode('draft');
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include variant('.primary') {
background-color: green;
}
}
+mode('strict')
+component('button')
+register
+variant('.primary')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
@include mode('strict');
@include component('button') {
@include register() {
@include variant('.primary');
}
@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 mixins and functions used in the code above: mode, component, default, variant.
Variant
- SASS
- SCSS
- SASS
- SCSS
+mode('draft')
+component('button')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
@include mode('draft');
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include variant('.primary') {
background-color: green;
}
}
+mode('strict')
+component('button')
+register
+variant('.primary')
+default
display: inline-block
background-color: grey
+variant('.primary')
background-color: green
@include mode('strict');
@include component('button') {
@include register {
@include variant('.primary');
}
@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 mixins and functions used in the code above: mode, component, default, variant.
State
- SASS
- SCSS
- SASS
- SCSS
+mode('draft')
+component('button')
+default
display: inline-block
background-color: grey
+state(':hover')
background-color: yellow
@include mode('draft');
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include state(':hover') {
background-color: yellow;
}
}
+mode('strict')
+component('button')
+register
+state(':hover')
+default
display: inline-block
background-color: grey
+state(':hover')
background-color: yellow
@include mode('strict');
@include component('button') {
@include register {
@include state(':hover');
}
@include default {
display: inline-block;
background-color: grey;
}
@include state(':hover') {
background-color: yellow;
}
}
button {
display: inline-block;
background-color: grey;
}
button:hover {
background-color: yellow;
}
The mixins and functions used in the code above: mode, component, default, state.
Context
- SASS
- SCSS
- SASS
- SCSS
+mode('draft')
+component('button')
+default
display: inline-block
background-color: grey
+context('ui-modal')
display: block
@include mode('draft');
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include context('ui-modal') {
display: block;
}
}
+mode('strict')
+component('button')
+register
+context('ui-modal')
+default
display: inline-block
background-color: grey
+context('ui-modal')
display: block
@include mode('strict');
@include component('button') {
@include register {
@include context('ui-modal');
}
@include default {
display: inline-block;
background-color: grey;
}
@include context('ui-modal') {
display: block;
}
}
button {
display: inline-block;
background-color: grey;
}
ui-modal button {
display: block;
}
The mixins and functions used in the code above: mode, component, default, context.
Responsive
- SASS
- SCSS
- SASS
- SCSS
+mode('draft')
+component('button')
+default
display: inline-block
background-color: grey
+responsive('html.mobile-portrait')
display: block
@include mode('draft');
@include component('button') {
@include default {
display: inline-block;
background-color: grey;
}
@include responsive('html.mobile-portrait') {
display: block;
}
}
+mode('strict')
+component('button')
+register
+responsive('html.mobile-portrait')
+default
display: inline-block
background-color: grey
+responsive('html.mobile-portrait')
display: block
@include mode('strict');
@include component('button') {
@include register {
@include responsive('html.mobile-portrait');
}
@include default {
display: inline-block;
background-color: grey;
}
@include responsive('html.mobile-portrait') {
display: block;
}
}
button {
display: inline-block;
background-color: grey;
}
html.mobile-portrait button {
display: block;
}
The mixins and functions used in the code above: mode, component, default, responsive.
Sub-element
TODO
Separator
TODO
Element
TODO
Pseudo-element
TODO
Register
TODO
Mutations
TODO
Element structure
TODO
Exclusive values
TODO
Aliases
TODO
Sub components
TODO
Mode
TODO
Specificity
TODO
Element specificity
TODO
Class specificity
TODO
Id specificity
TODO
Override
TODO
<a class="link">Link</a>
<button class="btn">
<span class="icon">
<img src="{{ buttonImage }}" />
</span>
<span class="text">
Button name
</span>
</button>
<div class="card">
<div class="header">
<h3 class="title">Header</h3>
</div>
<div class="content">
<button class="btn">
<span class="icon">
<img src="{{ buttonImage }}" />
</span>
<span class="text">
Button name
</span>
</button>
</div>
</div>
.btn {
display: inline-block;
&.primary {
background: #dda132;
.icon {
position: relative;
top: -2px;
}
.text {
text-shadow: 0 2px black;
}
}
}
.card {
display: inline-block;
border-radius: 1em;
padding: 16px;
.btn {
padding: 5px 10px;
margin: 10px;
}
&.selected {
outline: 2px solid gray;
box-shadow: 0 0 2em black;
}
}
<div class="card">
<div class="header">
<h3 class="title">
<div class="content">
<button class="btn">
<span class="icon">
<img>
<span class="text">
+component('.carousel')
...
+_________________________
+pseudo-element('before')
...
+state(':hover')
property: value
+component('.btn')
...
+_____________________
+element('.text')
...
+state('.primary')
property: value
.btn.primary .text {
property: value
}