The source code for the Code Computerlove Ghost CMS theme and the build tasks used to create it.
- Before you begin
- Gulp
- CSS
- Setup
- Project conventions
- Handling Internet Explorer
- Media queries
- Working with colours
- Working with units
- Grid system
- SCSS linting
- JS
##1. Before you begin
This repository does not include a Ghost installation. You will need to download and install Ghost in a separate directory. You can then symlink your _dest
folder to the Ghost themes folder like so:
ln -s <path/to/repositories>/code-ghost-theme/_dest/ <path/to/ghost-installation>/content/themes/code-ghost-theme
You will need to install Gulp, Ruby, and the Ruby gem Bundler before you can get started.
When you're ready, run the following commands:
bundle install
npm install
##2. Gulp
In Gulpfile.js
, config and tasks have been abstracted into their own files, for clarity and modularity. All project directories and paths are set in their own files, and accessed via the config
variable.
When making alterations use existing variables (or create new ones when necessary) rather than entering a static path.
There are two flags which can be passed through to gulp tasks:
--watch
- Enables watch tasks and livereload--prod
- Enables minification, concatenation, inlining etc.
Watch tasks pipe the event to gulp-livereload (with the livereload script included in the page locally, preferably using the LiveReload extension).
The gulp file has three tasks:
default
- Used by running justgulp
, this runs the development tasks. Add the--watch
flag for livereload, or--prod
for production compiles.package
- Runs thedefault
task and then zips up the_dest
folder ready for deployment. The--prod
flag must be used if you want a production ready package.watch
- Watches for changes in CSS / JS / HBS files for changes and recompiles and reloads the page.
###2.1 Adding tasks
Each task has it's own file, which pushes tasks into an array stored in the main gulpfile.js
. Watch tasks should be wrapped in a conditional.
tasks.default.push('sass');
if(config.isWatched) {
tasks.watch.push('watch:sass');
}
##3. Styles
This project uses Sass, a CSS pre-precessor. Features such as mixins are used throughout the project to help handle varying contexts.
###3.1 Setup
The main file for the project is main.scss
and should be manually updated with any new file references.
During Gulp SASS compilation, plugins such as gulp-autoprefixer and gulp-pxrem augment the compiled css.
###3.2 Project conventions
This project follows an ITCSS methodology.
Name-spaced Block Element Modifier (BEM) format is being used throughout to name class and variable names. Single hyphens can be used in blocks, elements or modifiers to make names more readable.
.o-block__element--modifier {}
.o-block__element--modifier-name{}
Following the ITCSS methodology the name-spaces are as follows:
.o- /* objects */
.c- /* components */
.u- /* trumps (or utility classes) */
File names should match class names, so future devs can find source code more easily. E.g. .o-block
will be found in _objects.block.scss
.
####Nesting
DON'T. One class should not depend on another. Classes should be portable without needing specific parents or grandparents. Specificity is also kept as low as possible this way.
Wrong:
.o-block {
.o-block__element {}
}
Right:
.o-block {}
.o-block__element {}
Use indenting to help give structure to related classes.
Only use nesting for pseudo elements and states such as :before
and :hover
, where you need to override a class based upon a dependancy or if you are using a BEM modifier to apply modified styles.
Acceptable:
.o-block {
&:hover {}
.no-js & {}
}
.o-block--modifier {
.block__title {}
}
####Elements
Elements are like children, they deserve names. Unless an element is truly generic and uses a global style, give it a name.
"Your selectors should be as explicit as your reason for wanting to select something." — Harry Roberts
Wrong:
.block {
h1 {}
}
Right:
.block {}
.block__title {}
####Sibling and child selectors
It's OK to select an element using :first-child
or +
etc if it's necessary and appropriate. Think about the use case. Will the target always be the first child? What if it moves?
####Extend
DON'T. Extending creates relationships between rules based on shared traits that are purely coincidental. It makes it difficult to inspect an element and can disguise how bloated your rules are. USE A MIXIN INSTEAD.
Learn more:
When to use @extend; when to use a mixin [Harry Roberts] Sass: Mixin or Placeholder? [Hugo Giraudel]
###3.3 Handling Internet Explorer
We're currently supporting IE9+, but optimising for Edge.
###3.4 Media queries
Media queries are managed using mixins and using defined breakpoint SASS variables. A map of breakpoints can be found in _settings.breakpoints.scss
.
The three mixins to handle media queries are:
@include respond-min(sm) {
//styles applied to screens fitting width of sm and up
}
@include respond-max(md) {
//styles applied to screens fitting width of md and down
}
@include respond-min-max(sm, lg) {
//styles applied to screen that width fit between md and lg
}
###3.5 Working with colours
A list of all the colours can be found in _settings.colors.scss
. Colour variables should not be accessed directly, but through the get-color
function via the $color-uses
map. E.g.
color: get-color(primary); // primary => code-blue => #1f2040
Optionally, you can lighten or darken using the get-color function. E.g.
color: get-color(primary, lighten, 20%);
###3.6 Working with units
When setting the dimensions of something in the styles, there is a SASS function to set the dimension using relative EMs (REMs) from a pixel measurement. E.g.
font-size: rem(16px);
The root HTML element has a REM size set, which changes at the sm
, md
and lg
breakpoints. You can find these sizes in _settings.breakpoints.scss
or use the get-rem-size(sm)
function.
gulp-pxrem handles adding a pixel fallback during css compilation.
###3.7 Grid system
The grid code is generated by a Sass mixin, in _trumps.widths.scss
. Columns are set per breakpoint, as required.
Column widths are expressed as fractions. Examples are: .u-1/2
, .u-1/3
and .u-1/4
. Column classes can be overridden per screen size, using the @md
and @lg
modifiers.
To float columns, use a container with .o-layout
and then either o-layout__left
or o-layout__right
.
Example:
<div class="o-layout">
<div class="o-layout__left u-1/2">...</div>
<div class="o-layout__left u-1/2@md">...</div>
<div class="o-layout__left u-1/2 u-1/4@lg">...</div>
</div>
###3.8 SCSS linting
This project uses SCSS Linting to help enforce BEM standards and maintain code quality. SCSSLinting is run during gulp dev
and gulp prod
. It is not ran as part of gulp watch
due to the long time it takes to complete. You can use the Sublime Text 3 package or a similar Atom package to catch warnings and errors during development.
The linting rules can be found in the .scss-lint.yml
.
Linting errors are displayed in the command line output.
##4. JS
This project uses vanilla JS rather than jQuery. Javascript that does not come from a vendor should be put in js\app.js
.
All JS files are concatenated (and minified then inlined for production) into a single file. Linting is done using gulp-jshint.
A gulp scripts:lint
task is also ran as part of gulp dev
and gulp prod
. Use the Sublime Text 3 package or a similar Atom package to catch warnings and errors during development.
Warning counts are displayed in the command line output.