SPOT CSS
{Single Place Of Truth Methodology}
The ultimate SASS framework and CSS methodology for sustainable styling.
Aspiring Industry Standard for Writing Styles
A clear and semantic way to write CSS
Imagine the most readable and simplest SASS code possible.
Imagine that everyone writes the same code, because the framework will not allow otherwise.
Imagine finding everything in a straight line, even blindfolded.
Imagine maintaining control even when a project or team scales.
You can just stop imagining it ;)
---Johnny Seyd, author of SPOT CSS
A Common Component Code:
- SCSS
- CSS
.btn { display: inline-block; padding: 0.5em 1.5em; background: $col-btn-bg; &:hover { background: $col-btn-bg-hover; } line-height: 1.5; .icon { display: inline-block; width: 1.5em; height: 1.5em; opacity: 0.75; &::before { content: '\0192'; display: inline-block; font-family: icons; opacity: 0.8; } } &:hover .icon { transform: scale(1.15); } &:hover .icon::before { opacity: 1; } .text { white-space: nowrap; vertical-align: middle; } &.primary { background: $col-btn-bg-prim; &:hover { background: $col-btn-bg-hover-prim; } .icon { opacity: 1; &::before { text-shadow: 0 0 0.5em black; } } .text { font-weight: bold; } } .modal & { display: block; .icon { opacity: 0.85; } } @include less-than(md) { padding: 0.25em 1em; .icon::before { transform: scale(1.25); } .text { white-space: normal; } }}
.btn { display: inline-block; padding: 0.5em 1.5em; background: #AA5500;} .btn:hover { background: #BB6600;} .btn { line-height: 1.5;}.btn .icon { display: inline-block; width: 1.5em; height: 1.5em; opacity: 0.75;}.btn .icon::before { content: '\0192'; display: inline-block; font-family: icons; opacity: 0.8;}.btn:hover .icon { transform: scale(1.15);}.btn:hover .icon::before { opacity: 1;}.btn .text { white-space: nowrap; vertical-align: middle;}.btn.primary { background: #55AA00;} .btn.primary:hover { background: #66BB00;}.btn.primary .icon { opacity: 1;}.btn.primary .icon::before { text-shadow: 0 0 0.5em black;}.btn.primary .text { font-weight: bold;} .modal .btn { display: block;}.modal .btn .icon { opacity: 0.85;}@media (max-width: 720px) { .btn { padding: 0.25em 1em; } .btn .icon::before { transform: scale(1.25); } .btn .text { white-space: normal; }}
Zoom:
⇐
On the left side is the code of the component written in the usual way in SCSS.
⇒
On the right side is the same code re-written in our way in SPOT CSS and SASS syntax.
Both codes generate the same CSS output, only some selectors will be in a different order.
The whole code on the right is organized by component elements. So the root element of the .btn
component is in a single block.
The order of the selectors is preserved, so the resulting CSS effect remains the same.
We use the SASS syntax because we are ussing so many mixins that @include
would flood the whole code. Instead, we can use only +
in the SASS syntax.
Pseudo-element also has its own code block and all CSS is here together in one place.
The advantage of this approach is that I can be sure that I won't find code that affects this element anywhere else. This is all I can rely on.
Compared to the left side, where the code is broken into many parts. Such code is hard to evaluate and you are never sure if there is another code that I have not yet seen that affects this element.
Note also the semantic aspect of this framework. Each mixin directly says what it does and for each single line it is possible to infer its purpose.
SPOT CSS is so intuitive that even someone who sees such code for the first time, even without a manual, naturally understands it.
Writing is more complicated, but it can be learned very quickly.
The +register
section is very useful and over time as a developer you will get used to looking at this code first to get an overview of the whole component as quickly as possible. It is self-documenting.
At the same time it is a functional thing and the framework checks and warns you if you have used only those things you have registered, and vice versa you have not registered something you have not even used.
And this section is important for the framework to be able to construct selectors in the right way.
So as a result, it is the same CSS code, but in SPOT CSS it is tidy, semantic and clear.
Every developer basically writes the same code, the framework watches to see if it breaks the rules, and thus teaches how to write correctly.
We often compare it to defragmenting a hard disk, where the same data is just arranged differently so that it can be read faster. Here it works similarly, plus semantics is added to the selectors.
The SPOT CSS Way:
- SASS
- SCSS
- CSS
+component('.btn') +register +state(':hover') +variant('.primary') +context('.modal') +responsive('{<md}') +element('.icon') +pseudo-element('before') +element('.text') +default display: inline-block padding: 0.5em 1.5em background: $col-btn-bg line-height: 1.5 +state(':hover') background: $col-btn-bg-hover +variant('.primary') +default background: $col-btn-bg-prim +state(':hover') background: $col-btn-bg-hover-prim +context('.modal') display: block +responsive('{<md}') padding: 0.25em 1em +_____________________________ +element('.icon') +default display: inline-block width: 1.5em height: 1.5em opacity: 0.75 +state(':hover') transform: scale(1.15) +variant('.primary') opacity: 1 +context('.modal') opacity: 0.85 +_____________________________ +pseudo-element('before') +default content: '\0192' display: inline-block font-family: icons opacity: 0.8 +state(':hover') opacity: 1 +variant('.primary') text-shadow: 0 0 0.5em black +responsive('{<md}') transform: scale(1.25) +_____________________________ +element('.text') +default white-space: nowrap vertical-align: middle +variant('.primary') font-weight: bold +responsive('{<md}') white-space: normal
@include component('.btn') { @include register { @include state(':hover'); @include variant('.primary'); @include context('.modal'); @include responsive('{<md}'); @include element('.icon') { @include pseudo-element('before'); } @include element('.text'); } @include default { display: inline-block; padding: 0.5em 1.5em; background: $col-btn-bg; line-height: 1.5; } @include state(':hover') { background: $col-btn-bg-hover; } @include variant('.primary') { @include default { background: $col-btn-bg-prim; } @include state(':hover') { background: $col-btn-bg-hover-prim; } } @include context('.modal') { display: block; } @include responsive('{<md}') { padding: 0.25em 1em; } @include ___________________________; @include element('.icon') { @include default { display: inline-block; width: 1.5em; height: 1.5em; opacity: 0.75; } @include state(':hover') { transform: scale(1.15); } @include variant('.primary') { opacity: 1; } @include context('.modal') { opacity: 0.85; } @include ___________________________; @include pseudo-element('before') { @include default { content: '\0192'; display: inline-block; font-family: icons; opacity: 0.8; } @include state(':hover') { opacity: 1; } @include variant('.primary') { text-shadow: 0 0 0.5em black; } @include responsive('{<md}') { transform: scale(1.25); } } } @include ___________________________; @include element('.text') { @include default { white-space: nowrap; vertical-align: middle; } @include variant('.primary') { font-weight: bold; } @include responsive('{<md}') { white-space: normal; } }
.btn { display: inline-block; padding: 0.5em 1.5em; background: #AA5500; line-height: 1.5;}.btn:hover { background: #BB6600;}.btn.primary { background: #55AA00;} .btn.primary:hover { background: #66BB00;} .modal .btn { display: block;}@media (max-width: 720px) { .btn { padding: 0.25em 1em; }}.btn .icon { display: inline-block; width: 1.5em; height: 1.5em; opacity: 0.75;} .btn:hover .icon { transform: scale(1.15);} .btn.primary .icon { opacity: 1;}.modal .btn .icon { opacity: 0.85;}.btn .icon::before { content: '\0192'; display: inline-block; font-family: icons; opacity: 0.8;}.btn:hover .icon::before { opacity: 1;}.btn.primary .icon::before { text-shadow: 0 0 0.5em black;}@media (max-width: 720px) { .btn .icon::before { transform: scale(1.25); }}.btn .text { white-space: nowrap; vertical-align: middle;}.btn.primary .text { font-weight: bold;} @media (max-width: 720px) { .btn .text { white-space: normal; }}
Pros
Clear to read
Even those who are not familiar with our methodology and framework understand the written code, because it has an intuitive syntax.
Easy to write
Because every line has a clear meaning and context, you can learn syntax very quickly and parser will help you not to break the rules.
Natural to think that way
You are already thinking in term of components, elements, states, variants an contexts. Just write and read it just like that.
Just SASS
Pure SASS framework with no other dependencies, javascript, or pre/post processor. So it's usable in any ecosystem (JS, PHP, Rubby, Python...)
Cons
SASS syntax
Everyone uses the SCSS syntax. So why SASS? Because of @include
. But you can use the SCSS syntax if you want. But it's ugly and cluttered. See the differences.
New mixins
SPOT CSS code is based on mixins and functions. Most of them produce selectors. It's easy to learn because they are semantic. See the Quick start guide.
No utility classes
If you are used to using utility classes exclusively, this approach is different. It is component based. But you can use utility classes sometimes, but in moderation.
Too strict
SPOT CSS framework forces you to follow many rules and will yell at you when you break some of them. But that's the advantage, it's basically an implicit linter. You can get around partially this with +mode('draft')
.
Other benefits
Universal
The SPOT CSS approach is compatible with most types of projects and methodologies. Even with BEM. What it is not compatible with is utility first class frameworks.
Standardized
With SPOT CSS basically everyone writes the same code. Or at least very similar. Therefore, it can serve as a company standard for how to structure and write CSS.
Easy to refactor
If your project is written as component based CSS, but over time you've gotten into an unmaintainable state, refactoring to SPOT CSS is a matter of a few days.
Alternative to utility class madness
If you don't like the approach of utility first frameworks like Tailwind and you don't want to write inline styles on steroids, which is easy to write, but damn hard to read and maintain, there is no need to convince you more.
Join our SPOT CSS community
and enjoy prompt support on
The fastest way to find out instantly
how to do something is to use
CHEAT SHEET
Installation
Install spotcss in your project:
- NPM
- Yarn
$ npm i --save-dev spotcss
$ yarn add --dev spotcss
Usage
Import spotcss framework in your style:
- SASS
- SCSS
@import "~spotcss"
@import "~spotcss";
Let's try it!
Fly through the quick start guide and
see what it is about in a few minutes:
Take a tour through the tutorial and
learn the basic principles in one hour:
Explore the complete SPOT CSS
framework documentation:
What's coming next...
Linter for VS Code
Although the SPOT CSS framework has an implicit linter, errors and warnings are raised into console after the code is compiled. A linter to an IDE such as VS Code would make writing code much more pleasant.
SPOT Editor
We already have a special editor on our stylers.cloud platform for crowdsourced stylers to write SPOT CSS, but we are thinking about launching it as a standalone tool. If you are interested, you can join our waitlist.
Refactor manual
We are preparing a step-by-step manual on how to refactor your old tangled (S)CSS code into SPOT CSS and it will be published here soon. But the best part is that you can also order the refactor of your CSS code from us. The whole delivery is a matter of a few days and it's 100% safe.
Converters
Since the way of writing SPOT CSS is deterministic and semantic, it is possible to export to most other methodologies or import from other methodologies. We could even make an export to Tailwind.
Autogenerated UI Kit Doc
Thanks to the code semantics in SPOT CSS it is also possible to automatically build documentation of components written in SPOT CSS. Something like Storybook. Autogenerated site where developers could see and try all components and their variants.
Looking for contributors
If you are interested in any of this and you know how to code in JS or SASS, we can make amazing and useful tools together. I'm on my own for now on this project and any help will be appreciated. Contact me on our Discord - Johnny Seyd (@Seyd#6245)