A Project code guideline
Jun 14, 2016
This is my documentation for a new project. I’m writing it here rather than inside a repository so that it can exist across multiple projects. This will eventually be duplicated on to a markdown document for posterity!
Project structure
As the project is Django based, the front end files (CSS, Javascript and Media) can live in a few places — The Project folder, the static folder and every app’s static folder.
Project
1project └── front-end * Compliation and post processing * └── gulpfile.js └── package.json └── node_modules * source files * └── src └── _templates └── full-templates.html └── components └── component-only.html └── scss * See sass folder structure example * └── images └── js └── temp-media * Compliled files from source* └── css static └── css └── js └── images app └── static └── css └── js └── images
SCSS / Styles
1scss └── variables └── constructors └── mixins └── resets └── placeholders └── site └── utils └── grid └── core └── header └── footer └── * e.t.c * └── components └── * Main components * └── related-content └── section * to be split out on go live * └── blogs └── articles └── * e.t.c * └── overrides └── state
Files with scope of the whole site (e.g. resets, header/footer, shared components) all must live within the project > front-end directory. Once the project is integrated the global files will also be copied into the main static folder as Django won’t ever know to look within the front-end path.
App specific code can live in the relevant apps, however as there is so much shared assets in the project sass it makes sense generally to keep it all in the main project folder and copy it. For Javascript it’t not so much of an issue as there are no variables, libs or mixins to include!
Technology used
HTML
- Just good old HTML5
- Schema and a few other bits for SEO.
CSS
For this project and others going forward, I made the decision to use flexbox where at all possible. This has a few implications, however based on visitor usage there are less that 2% of browsers that don’t support the property, and conveniently there is a polyfill and a PostCSS plugin to fill in the gaps.
Javascript
- jQuery
- Hammer Time for swipe and mobile action detection!
Naming conventions,methodologies and other CSS
The project is split between using BEM style coding for individual modules and single function classes for everything else. There are also a few state classes that can be applied to modules or to the body.
Naming conventions
CSS class names should use hyphens to separate words. Any class that is applicable to a media-query should be suffixed with the breakpoint name and an @. Classes (especially single function classes) should be prefixed with a good indication of what they do. If you can’t understand what a class does then it’s not named good!
The Derek Zoolander School for Kids Who Can’t Read Code and Want to Do Other Dev Stuff Good Too
1<div class="visibility-hide@m visibility-show@xl"> Example content </div>
1// Basic Naming .this-is-a-class-name {...} // Correct .thisIsABadName {...} // Incorrect .this_is_also_wrong {...} // Incorrect // Prefix classes with use case .font-size-double-pica {...} .letter-spacing-condensed {...} // Classes that apply to a media query should have the relevent suffix // Note that the breakpoint mixin should be used ...and... // That when making a class the @ needs to be escaped with a \ or Sass throws a wobbler // Classes on the HTML don't need the \ @include breakpoint(m) { .visibility-hide\@m {...} } @include breakpoint(xl) { .visibility-show\@xl {...} }
Specificity
CSS should be written to have a low a specificity as possible. Ideally just 1 class deep. This means that where a class needs to be overridden on the odd occasion (e.g. theming for other sections) there is only one extra class to add. If !important needs to be used, or a selector chain is too long then something has gone wrong. Stop and see if what you are trying to override can be changed, instead of making the problem worse!
It’s OK to have selectors with obvious elements in like a li, because if the element class us a ul
or ol
then that’s the only ever element that could be there. In fact I insist that you should so it this way because adding classes to every <li>
is tedious and makes the HTML heavier and harder to read.
There are times when !important is, well, important and should be used. For example an error text should be bold and red.
1.my-class {...} // All gravy! .my-class .sub-class {...} // Not so good .my-class .sub-class .other-class span em i {...} // You're fired // Using BEM methodology keeps the risk of such selector chains to a minimum, but you never know! // Applied to a <ul> - Correct .block__element li {...} // It's OK to have selectors with obvious elements in like a li // Applied to a <li> - Incorrect .block__element-item {...} // Don't make classes for li because it's just a waste of HTML and time! //Error .has-error { color: red !important; font-weight: 800 !important; siren: loud !important; // JK - this is a made up property! }
Loose coupling
Styles should not be tied directly to a HTML element. Instead, a class should be used so that if the element has to be changed (e.g. for changing SEO reasons: h1 to h2) then the change doesn’t require CSS to be touched. This also applies to Javascript classes. Elements should not be styled based on the Javascript class. This could be renamed, moved or removed at any point in the future. Tread lightly — It doesn’t matter what the elements are behind the scenes, the CSS is just to make them look a certain way whatever they are!
1<!-- Doesn't matter what the elements are --> <h1 class="heading">Heading</h1> <h2 class="subheading">Sub heading</h2> ... <ul class="list"> <li>...</li> </ul> <div class="meta">...</div> <!-- Doesn't matter what the elements are --> <h3 class="heading">Heading</h3> <span class="subheading">Sub heading</span> <ol class="list"> <li>...</li> </ol> ... <strong class="meta">...</strong>
1// Good .heading {...} .sub-heading {...} .meta {...} .list li {...} // Bad h1 {...} h2 {...} div {...} // Also Bad h1.heading {...} h2.sub-heading {...} div.meta {...}
BEM Methodology
BEM is a CSS naming methodology which aims to make the class names as clear as possible to read, keep the styles tightly name-spaced and give a good sense for which module a class applies. More about the key concepts and naming convention can be found on the linked articles.
A simple example can be found below.
1<!-- BEM Styled Sass example --> <!-- Example --> <!-- Unmodified Block --> <div class="block"> <div class="block__element"> </div> </div> <!-- Modified Block --> <div class="block block--modifier"> <div class="block__element"> </div> </div> <!-- Modified Element in Block --> <div class="block block"> <div class="block__element block__element--modifier"> </div> </div> <!-- Real world example with all modifiers applied--> <form class="search search--micro"> <input type="text" class="search__field search__field--featured"> <input type="submit" value="Search" class="search__submit search__submit"> </form>
1// BEM Structured Sass - example .block { // .block {...} &--modifier { // .block--modifier {...} } &__element { // .block__element &--modifier { // .block__element--modifier {...} } } } // BEM Structured Sass - Real world module .search-bar { // .search-bar &--micro { // .search-bar--micro } &__field { // .search-bar__field &--featured { // .search-bar__field--featured } } &__submit { // .search-bar__submit } }
Single function classes & utility classes
Single function classes have the purpose of having a very tightly defined function. The major ones that can be found on the project are aimed at visibility and font-sizes. Font family and colour could be single function classes, but ideally they should be more tightly controlled and so it at a site, page or module level.
1<h1 class="font-size-double-paragon"> <a href="#">I'm a big bit of text!</a> </h1>
1.font-size-double-paragon { font-size: 2.5rem; line-height: 1.4; } // Breakpoint mixin for medium(screen) @include breakpoint(m) { .font-size-double-paragon\@m { font-size: 4.5rem; line-height: 1.5; } } // Output: // @media screen and (min-width: 650px ) { // .font-size-double-paragon\@m { // font-size: 4.5rem; // line-height: 1.5; // } // }
Utility classes are very similar to single function classes with the exception that they only do one thing such as floating left.
Grid classes
The grid is 24 column based for all viewports except mobile which uses 12. Grids are wrapped in a containing class to keep everything hunky-dory.
A grid element should have the .grid__item class and then the helper classes for the different grids that apply. .gi- is used just to keep class names a bit less wordy!
1<div class="grid"> <div class="grid__item gi-full gi-1/2@m gi-1/3@l"> <div class="gi-guttered"> ... inner content ... </div> </div> </div>
1// Grid wrapper .grid {...} // Grid item .grid__item {...} // Grid classes to control the columns // Grid classes all have quick helper versions for ful, half, third and quarter widths // Note the \ escaping of the slash and at symbols. These are not needed in the HTML .gi-full {...} .gi-1\/2\ {...} .gi-1\/3\ {...} ... @include breakpoint(m) { .gi-1\/2\@m {...} ... } @include breakpoint(l) { .gi-1\/3\@l {...} ... } // Then the full set for all column spans ... .gi-7\/24 {...} ... .gi-19\/24 {...} // And then media queried versions // ... you get the point ...
Human Content
One of the hardest things to do is to teach admins/users to put the right classes on things, so the best thing to do is to avoid that situation full stop. A class of .human-content can be applied to any wrapper where an editor or user can create content that has formatting (headings, links, tables, lists, em and strong … ).
1<div class="some-module-class-or-whatever"> <div class="human-content"> <!-- content here is populated by the CMS --> <h2>A title</h2> <p>Lorem...</p> <p><strong>Ipsum...</strong></p> <table>...</table> <div class="responsive-table-wrapper-class">...</div> </div> </div>
1// Any content written by a human (editor or user) that has formatting should use this class on the wrapper .human-content { // Lots of these selectors are examples. // The real world class uses selector-placeholders and mixins to build the full combination // h1 should never be allowed by the CMS because people will use it wrong! h2, h3, h4, h5 {...} h2 {...} ... p + p, ul + ul, p + ul, .responsive-table-wrapper-class + .responsive-table-wrapper-class {...} //e.g. margin-top: 1rem; strong {...} .responsive-table-wrapper-class {...} }
Variables
Variables should be prefixed with the property they are designated for. So $font-size refers to a font-size, and $color-brand is for a colour.
Breakpoint naming and media queries
Breakpoints we’re going to be named around mobile, tablet, desktop but what about big tablets, small desktops e.t.c. The naming convention is:
- xs (e.g. very small mobiles)
- s (e.g.mobiles)
- m (e.g.tablets)
- l (e.g.desktops, big tablets)
- xl (e.g.big desktops, iPad pro…)
- xxl (e.g.very big desktops)
Breakpoints should be used via the @include breakpoint(m) mixin, which can also specify a max width via @include breakpoint(s, xl). They are currently designated in px, however this will be migrated to em/rem in the future.
Classes where a breakpoint applies should use the breakpoint as a suffix: e.g. .some-class@m, .other-class@xl, .new-class@m-xl. This ties in more with single use classes rather than modules and components as a component shouldn’t generally need helper classes to react to breakpoints. Components can use helper classes like grids in their markup
Fonts
Instead of coming up with a silly naming convention for font sizes, the names are based off the (closest) point size name once a pixel value has been converted into points. The resulting font size names are as follows (TBC):
- fiveline-pica
- double-paragon
- double-english
- great-primer
- english
- pica
- small-pica
- bourgeois
These can either be applied using the placeholder %font-size-[size name] or as a class to an element using .font-size-[size name]. The sizes are in REMs. Alternative sizes can be used for unique modules such as stats, but in general the core font sizes should be used.
Fonts families are only available via a placeholder. This is because there should be some tighter control on which fonts are used on which elements
Forms
Forms require a huge amount of markup to allow them to be flexible in many ways. Either they controlled tightly within grid items, or they have an enforced set of markup that can take care of all the various ways they are shown.
1# Full Form Structure form └── section | └── legend | └── grouped-fields [--inline] | | └── grouped-label | | └── grouped-content | | └── * single-field-markup | | └── custom-other-markup | * | └── single-field (various content orders) | └── single-label | └── single-input | └── custom-other-markup | | or | └── custom-field-[wrapper] (various content orders) | └── custom-label | └── custom-input | └── custom-other-markup └── actions └── form-button └── form-button # Grouped field example with hidden labels and a group title form └── section | └── *grouped-fields [--inline] | | └── grouped-label | | └── grouped-content | | └── single-field | | └── single-label [.sr-only] | | └── single-input | | └── single-field | | └── single-label [.sr-only] | | └── single-input | | └── custom-other-markup | | └── single-field | | └── single-label [.sr-only] | | └── single-input | | └── custom-other-markup | └── single-field | └── single-label | └── single-input └── actions └── form-button └── form-button