Our team specialize in complex web and hybrid mobile applications. 99% of our projects are built with AngularJS. For last two years we have been working on conventions to simplify our development process. In this repository we try to gather different utilities which simplify bootstrapping, developing and maintaining scss part of all our projects. In addition to these utilities we use Autoprefixer
We have reached stable version of our css conventions and ready to share them with community. It is not our aim to build another scss framework. That is why we are starting to migrate our conventions to PostCSS which we believe is the future of css and can replace 80% of our scss framework
At the same time we hope that publishing our small scss framework will help somebody to save their development time and that we will get some feedback from other developers about how we can improve our workflow
Thank you and enjoy!
Inspired by RoR projects structure we selected the following structure for all our projects:
app/
resources/
images/
favicon/
...
...
fonts/
...
...
html/
layouts/
...
partials/
...
pages/
...
...
js/
...
scss/
...
Here we store images, fonts and other assets
html/layouts/
- Reusable wrappers a.k.a. general html components which wraps main html pages of the project
html/partials
- Reusable content a.k.a. general html components which are being wrapped by main html pages of the project
html/pages
- HTML pages for static content (like home, terms of use, privacy policy, etc...)
HTML pages for dynamic content (like sessions, users, etc...) are stored at the root of html
folder and are named by the following convention:
ResourceNameFolder/
SubResourceNameFolder/
...
index.html
show.html
new.html
edit.html
...
index.html
- HTML page to show all resources
show.html
- HTML page to show specific resource
new.html
- HTML page to create specific resource
edit.html
- HTML page to update specific resource
JavaScript conventions is not the subject of this repository
The main part is SCSS structure which is the following:
scss/
utilities/
mixins/
functions/
classes/
_variables.scss
vendor/
...
root/
...
_variables.scss
_utilities.scss
_vendor.scss
_root.scss
app.scss
html/
layouts/
...
partials/
...
pages/
...
...
app.scss
is the entry point for Sass preprocessor. We do not define any rules here. Its only purpose is to import needed framework dependencies in the specific queue and then import all files from the scss/html
folder:
@import 'utilities';
@import 'variables';
@import 'vendor';
@import 'root';
...
_utilities.scss
imports our default variables and all utilities which are divided into three main groups mixins, functions and classes. They are located inside utilities folder and are imported in the following queue:
@import 'utilities/variables';
@import 'utilities/mixins/class';
@import 'utilities/mixins/offset';
@import 'utilities/mixins/size';
@import 'utilities/mixins/font-size';
@import 'utilities/mixins/container';
@import 'utilities/mixins/background-image';
@import 'utilities/mixins/focus-hover';
@import 'utilities/functions/colors';
@import 'utilities/functions/inline-data-url';
@import 'utilities/classes/grid';
@import 'utilities/classes/rectangle';
@import 'utilities/classes/helpers';
utilities/_variables.scss
stores following technical variables:
If user do not specify custom app title, application will be simply called 'app'
$app: 'app' !default;
If user do not specify custom breakpoint, application will have 3 main breakpoints
$sm: 40em !default;
$md: 60em !default;
$lg: 80em !default;
We have alias for Sass boolean variables
$f: false;
$t: true;
utilities/mixins/_class.scss
is the main tool to apply our convention and to keep our code clean and maintainable. It is the core of our framework. That is why we will talk about it at the end
utilities/mixins/_offset.scss
simplifies work with offsets. Here are some things which you can do with mixins inside it:
.class {
@include position(('1': 12px), relative);
}
.class {
@include position((a: 12px), absolute, 1);
}
.class {
@include position((t: 12px, h: 0), fixed, -1);
}
.class {
@include margin(('!t': 12px));
}
.class {
@include margin((h: auto));
}
.class {
@include padding(('1': 18px, '4': 6px));
}
.class {
@include padding((v: 12px, l: 100%));
}
will be converted to
.class {
position: relative;
top: 12px;
left: 12px;
}
.class {
position: absolute;
top: 12px;
right: 12px;
bottom: 12px;
left: 12px;
z-index: 1;
}
.class {
position: fixed;
top: 12px;
right: 0;
bottom: 12px;
left: 0;
z-index: -1;
}
.class {
margin-right: 12px;
margin-bottom: 12px;
margin-left: 12px;
}
.class {
margin-right: auto;
margin-left: auto;
}
.class {
padding-top: 18px;
padding-right: 6px;
padding-bottom: 6px;
padding-left: 18px;
}
.class {
padding-top: 12px;
padding-left: 100%;
}
Moreover, you can use @include offset();
to build offsets with custom prefixes
.class {
@include offset((v: 3px solid #000, h: 1px solid #fff), border);
}
will be converted to
.class {
border-top: 3px solid #000;
border-right: 1px solid #fff;
border-bottom: 3px solid #000;
border-left: 1px solid #fff;
}
utilities/mixins/size.scss
allows you to declare container width
and height
and their limits in one line
.class {
@include size(100%, 100%);
@include size(300px, 600px, max);
@include size(2em, 2em, min);
}
will be converted to
.class {
width: 100%;
height: 100%;
max-width: 300px;
max-height: 600px;
min-width: 2em;
min-height: 2em;
}
utilities/mixins/font-size.scss
allows you to declare font-size
and line-height
. If you do not specify line-height
parameter, by default it will be set to 1.2
.class {
@include font-size(1.2rem, 100%);
}
.class {
@include font-size(1.2rem);
}
will be converted to
.class {
font-size: 1.2rem;
line-height: 100%;
}
.class {
font-size: 1.2rem;
line-height: 1.2;
}
If you want to split up window height in several parts, usually you will need to set up height: 100%;
to html
& body
containers and to all containers generated by different frameworks (e.g. <ui-view>
which is generated by ui-router
). Another way is to create fixed container which will fill full screen size. utilities/mixins/container.scss
allows you to create such container in one line. Moreover, this mixin allows you to enable/disable scrolling inside created container and enable/disable momentum scrolling inside it
.class {
@include container;
}
.class {
@include container($f);
}
.class {
@include container($t, $f);
}
will be converted to
.class {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.class {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
}
.class {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
}
NOTE#1: some JS libraries for scrolling or smooth scrolling will not with fixed containers
NOTE#2: there is an issue with fixed elements inside container with momentum scrolling being set. However, we think that keeping fixed elements inside fixed elements is a kind of bad practice and it should be avoided
utilities/mixins/background-image.scss
includes three mixins which allow you to set 3 background-image
to class, before class or after class to represent their default
, focus & hover
, active
states in one line
.class {
@include background-image(url#1);
}
.class {
@include background-image-before(url#1, url#2);
}
.class {
@include background-image-after(url#1, url#2, url#3);
}
will be converted to
.class {
background-image: url#1;
}
.class::before {
background-image: url#1;
}
.class::before:focus,
.class::before:hover {
background-image: url#2;
}
.class::after {
background-image: url#1;
}
.class::after:focus,
.class::after:hover {
background-image: url#2;
}
.class::after.__active {
background-image: url#3;
}
utilities/mixins/focus-hover.scss
is a simple alias for &:focus,&:hover
.class {
@include focus-hover {
color: white;
}
}
will be converted to
.class::before,
.class::after {
color: white;
}
utilities/functions/inline-data-url.scss
module was created to gather helpers for inline resources like inline svg icons or inline base64 images. There is only one helper now and its name is url-svg
.class {
background-image: url-svg(...);
}
will be converted to
.class {
background-image: url(data:image/svg+xml,...);
}
utilities/functions/colors.scss
module collects functions which work with colors
set-color
- creates map of shades of specific colorgray
- mixes black and white in proportionshade
- mixes custom color and black in proportiontint
- mixes custom color and white in proportionhex-symbols
- excludes '#' symbol from hex colors
$var: set-color(red);
$var: set-color(red, 25%, 10%, 10%, 25%);
$var: gray(50%);
$var: shade(red, 25%);
$var: tint(red, 25%);
$var: hex-symbols(#fff);
we return to
$var: (darkest: darken(red, 15%), darker: darken(red, 5%), default: red, lighter: lighten(red, 5%), lightest: lighten(red, 15%));
$var: (darkest: darken(red, 25%), darker: darken(red, 10%), default: red, lighter: lighten(red, 10%), lightest: lighten(red, 25%));
$var: gray;
$var: #bf0000;
$var: #ff4040;
$var: fff;
Inspired by bootstrap grid we created its lite 24 columns version and placed it inside utilities/classes/grid.scss
module. It includes only to main classes .row
and .col
. .col
class has different states which represent its size and offset. Bootstrap grid example with our grid will look as following:
<div class="row">
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
<div class="col __md-2">col-md-1</div>
</div>
<div class="row">
<div class="col __md-16">col-md-8</div>
<div class="col __md-8">col-md-4</div>
</div>
<div class="row">
<div class="col __md-8">col-md-4</div>
<div class="col __md-8">col-md-4</div>
<div class="col __md-8">col-md-4</div>
</div>
<div class="row">
<div class="col __md-12">col-md-6</div>
<div class="col __md-12">col-md-6</div>
</div>
NOTE: Our grid does the same things as bootstrap grid or any other float grid does. However, we believe that we simplified it as much as possible and left inside only those thing which are needed to implement responsive 24 column grid
NOTE: to use this module you need to uncomment its import inside utilities.scss
utilities/classes/rectangle.scss
allows you to create responsive rectangles and squares and even change their dimensions on breakpoints. Imagine that you need responsive rectangle which proportions will be 1:2 on mobile screens, 1:1 on tablet screens and 1:0.4 on desktop screens. The following code will create it for you:
<div class="rectangle __xs-200 __sm-200 __md-100 __lg-40">
<div class="rectangle_content">
Responsive rectangle content
</div>
</div>
NOTE: to use this module you need to uncomment its import inside utilities.scss
utilities/classes/helpers.scss
module were we collect small helpers to make our life easier
.clearfix
- clearfix hack by Nicolas Gallagher.ellipsis
- replaces long strings with ellipsis.hidden
- addsdisplay: none !important
to element. It is used by our JS developers as a standard class to hide elements.input-transparent
- we created to simplify inputs customisation. You can create relative container and create any layout for custom input inside it. The last element inside this container should be input with our class. Our class will make input invisible by addingopacity: 0;
&appearance: none;
and will force it to fill its relative parent by adding `@include position((a: 0), absolute, 1);
<div>
...
<input type="file" class="input-transparent">
<div>
variables.scss
stores our custom variables. We believe that creating variables for everything is bad practice and it complicates maintains. In most cases we stores here color maps for our color pallets and inline svg icons only
vendor.scss
imports vendor stylesheets. By default only one library is included it is sanitize.css. We used to work with different reset.css
and normilize.css
libraries. However they were not enough for us. Only sanitize.css
satisfies us in 100% and that is why we include it
root.scss
imports global project stylesheets like animations, fonts, etc. Also it includes some basic rules which we use in all project
- It disables default outline on focus
- It hides webkit scrollbar
- It sets root font size to 10px to simplify rem usage
- It sets default
font-family
,font-size
andline-height
tobody
Inside root/animations.scss
we define all css animations which will be used in project. By default it includes fadeIn
and fadeOut
effects
Inside root/fonts.scss
we define all font which will be used in project and create mixins to assign them easily. By default it does not import any font faces, but includes font-sans-serif
mixin which sets up sans-serif
as a font family. It is used by default in root.scss
to assign default font family to body
We believe that the best way to use svg icons is the following:
- Optimise it with svgo
- Encode it to url encoded format. We use urlencoder.org to do it
- Create function which will take as parameters parts of your svg which possibly will be changed a lot and will return encoded svg string. In most cases we separate as parameters svg icon colors
Inside root/icons.scss
we define all those svg functions. It is the only root module which is imported inside variables.scss
and not inside root.scss
Inside root/angular.scss
we define all stylesheets which are relevant to AngularJS
and its libraries. By default it three classes and do the following:
- disables
ng-animate
globally to use it with specific elements only - imports styles for ng-cloak
- forces 'Angular Google Maps' to fill parent container
NOTE: to use this module you need to uncomment its import inside root.scss
Inside root/mobile.scss
we define all stylesheets that helps hybrid mobile apps to look more natively. By default it includes three styles
NOTE: to use this module you need to uncomment its import inside root.scss
We believe that DRY principles do not work in css. Everyone was in situation when you had just finished refactoring in your css files, had remove each duplication in your code and made your code super DRY. However, just after you revived feedback from your client were he asked you to change button on one page and leave it as now on the other. Nightmare
We love using WET principles inside our css code. Such approach allows you to easily edit any attribute. However, if you have one css file, it becomes huge and hard to maintain. That is why we created our conventions and implemented them with sass
Inside scss/html
folder we stores scss which is grouped by the same principle as html inside html
folder
scss/
html/
layouts/
...
partials/
...
pages/
...
...
scss/html/layouts/
- Scss to create reusable wrappers a.k.a. general html components which wraps main html pages of the project
scss/html/partials
- Scss to create reusable content a.k.a. general html components which are being wrapped by main html pages of the project
scss/html/pages
- Scss to create HTML pages for static content (like home, terms of use, privacy policy, etc...)
Scss to create HTML pages for dynamic content (like sessions, users, etc...) are stored at the root of scss/html
folder and are named by the following convention:
ResourceNameFolder/
SubResourceNameFolder/
...
index.html
show.html
new.html
edit.html
...
index.html
- HTML page to show all resources
show.html
- HTML page to show specific resource
new.html
- HTML page to create specific resource
edit.html
- HTML page to update specific resource
Generally we do not use default '.' or '#' identifiers to create classes or ids inside our scss files. We use them only at edge cases. Instead, we created utility which allows us to follow our conventions. It consists of 3 main mixins
namespace
- allows you to define global (ids like #app) and local (classes like .form) namespacesclass
- allows you to create classes which names are related to their parentsstate
- allows you to create class states (classes like .__selected)
In one raw our convention can be written as following:
#<global-namespace-1>.__<state-1>.__<state-2>.__<...> #<global-namespace-2>.__<state-1>.__<state-2>.__<...> <...> .<local-namespace-1>.__<state-1>.__<state-2>.__<...> .<local-namespace-2>.__<state-1>.__<state-2>.__<...> <...> .<class-parent-1>_<class-parent-2>_<...>_<class>.__<state-1>.__<state-2>.__<...>
We always use nested selectors syntax inside our scss, because it increase readability. Our utilities fix the biggest problem of nested selectors - generated code complexity
Instead of TL;TR
guides we prepared an example which we believe explains everything
@include namespace('my-app', $t) {
height: 100px;
@include state('theme-1') {
background: red;
@include class('main') {
background: green;
}
}
@include state('theme-2') {
background: blue;
@include class('main') {
background: yellow;
}
}
@include class('header') {
height: 20px;
@include class('description') {
font-size: 12px;
}
}
@include class('main') {
background: orange;
height: 60px;
}
@include class('footer') {
height: 20px;
@include class('description') {
font-size: 12px;
}
}
@include namespace('navigation') {
width: 100px;
@include state('minified') {
width: 10px;
@include class('button') {
height: 10px;
}
}
@include class('button') {
height: 20px;
}
}
}
will be converted to
#my-app {
height: 100px;
}
#my-app.__theme-1 {
background: red;
}
#my-app.__theme-1 .main {
background: green;
}
#my-app.__theme-2 {
background: blue;
}
#my-app.__theme-2 .main {
background: yellow;
}
#my-app .header {
height: 20px;
}
#my-app .header_description {
font-size: 12px;
}
#my-app .main {
background: orange;
height: 60px;
}
#my-app .footer {
height: 20px;
}
#my-app .footer_description {
font-size: 12px;
}
#my-app .navigation {
width: 100px;
}
#my-app .navigation.__minified {
width: 10px;
}
#my-app .navigation.__minified .button {
height: 10px;
}
#my-app .navigation .button {
height: 20px;
}
We created this tool to optimize our workflow and published it to save other developers time. Feel free to use entire framework or its separate parts which you will find useful. We will appreciate any feedback which will help us to improve our development. Cheers!
- We created our code snippets for atom. You can find them inside
snippets.cson
file