An easy extendable & understandable Codebase, which performs well on all kind of devices. It should provide basic Helpers and In-Code Documentation to develop new features fast and comprehensible way. To achieve this, the Frontend uses the following Technology Stack:
- Grunt, (Website) for building a release of all resources and to optimize Files
- LESS (Website), to write less, more structured CSS-Code, which provides re-usable components
- require.js, (Website) to work with asynchronous JS-Modules, which are loaded, when they are needed
- Node/ NPM, (Website Nodejs, Website NPM) to install grunt and its dependencies. Run
npm install
. - Bower, (Website) to install Frontend Dependencies. Run
bower install
.
NOTE The commands npm install
, bower install
, grunt init
and grunt release
are included in buildout. As far as you not directly work with Icons, Less or Js
the preferred way of updating everything is to run bin/buildout -N
.
- Lots, lots, lots of Templates with Tons of Variants of the same Thing
- Many Sub-Projects (B2B, VEC, Events, ...)
- Legacy-Code
Make sure you have at least node with version v0.10.35
installed (it should
work with io.js
aswell - see installation steps on their website). Npm should
be minimum at 2.2.0
. To achieve this (on) Ubuntu, run the following commands
# Add Nodejs ppa & run Installation
curl -sL https://deb.nodesource.com/setup | sudo bash -
# Install nodejs
sudo apt-get install -y nodejs
# Update NPM
npm install -g npm
# Check Versions
npm -v
~ 2.1.18
node -v
~ v0.10.35
# Install global requirements
npm install -g grunt-cli bower
# Install local requirements
cd [project-root] && npm install
# Builds Development Enviroment
grunt init
# To update package.json & bower.json
grunt update-packages
# Builds Production Environment
# NOTE: This will read the project version number of the package.json and do
# some replacement within the js-files. If there is a rollout, make sure you ran
# `grunt update-packages before` and committed the changes. For information on
# how this works, see https://trac.wienfluss.net/wieninfo/browser/wieninfo-site/Gruntfile.js#L394
grunt release
While working on .less
and .js
files, the files have to be compiled on-the-fly
with Grunt. To listen for changes, use
grunt watch
# Only listen for changes in less
grunt watch:less
# Only listen for changes in js
grunt watch:js
Note: this only compiles the resources into the dev-enviroment. To create the
production-buidl, also run grunt release
.
Grunt does some simple string-replacement within configured Js-Files.
/* Gruntfile.js */
{
match: '${version}',
/* development */
replacement: ( new Date() ).getTime() + '',
/* production */
replacement: '<%= pkg.version %>',
},
{
match: '${requirejs_base_url}',
/* development */
replacement: '/++resource++wir/js/dev',
/* production */
replacement: '/++resource++wir/js/dist',
}
This allows e.g. the following
/* player.js */
var urlBase = '${requirejs_base_url}/lib/jwplayer',
defaults = {
html5player: urlBase + '.html5.js?v=${version}',
};
/* main.js */
require.config({
baseUrl: '${requirejs_base_url}',
})
The files, within these replacements are happening are defined in the Gruntfile, to keep it fast.
BEM stands for Block-Element-Modifier
and describes a way to structure CSS-Code in Class-Like Elements. The initial Idea was ivented by Yandex, but over the time it was modified by many Developers (e.g. single underscore instead of two). Beside semantic Advantages, it forces you to think about the HTML-Structure of a Widget first and avoids Tons of Hacks, needed because of !important
s and other CSS-Specificity related Problems.
- TIP Talk "CSS is a Mess" by Jonathan Snook
- Less-Files are located in
[..]/publicskin/resources/less/
(trac). The initial main-File ismain.less
- Variables are defined in
variables.less
(Colors, Font-Sizes, Fonts, Grid-Size, Z-Indexes, Mediaqueries) - Each File represents a corresponding Block (see BEM-Principles)
form/
(trac) contains Input-Types (Input, Buttons, Textarea, ...)hacks/
(trac) provides helpers, e.g. for older IEs and the filelegacy-flexbox.less
, which transforms the Flexbox-Layout into Floats.rtl/
(trac) contains adjustments needed for RTL-Languages (Arabic in our case)
variables.less
main.less
utils.less
Providesu--
several utilities such asu--is-accessible-hidden
,u--is-hidden
,u--clearfix
...utilsprint.less
Print-Utilities, to hide certain Elements ...infoscreen.less
CSS-Code for the Office-Infoscreen (Weather Information)
/* FILE: block1.less*/
/* BAD */
.block1 {}
.block1_module1 {}
/* GOOD */
.block1 {
&_module1 {
..
}
}
- Syntax
.block_module--modification
(Block-Name followed by a single underscore, Module-Name, optional dash-dash Modification-Name) - Block-Names, Module-Names, Modification-Names should not be camelCased, but dashed
/* BAD */
.codeBlock_container {}
.code_block_container {}
/* GOOD */
.code-block_container {}
Less-Syntax (Indention: 2 spaces):
.block {
&_module {
/* Module-Names should be as semantic as possible */
&--modification {
/* A Module-Modification could be e.g. a state (.block_module--is-hidden), color changes (.block_module--highlight), ... */
}
}
}
Nesting with referencing to its parent (with &
can be tricky in some cases)
/* BAD */
.block {
&_module {
&:hover,
&:focus {
.block_module {
...
}
}
}
}
/* BETTER */
.block {
&_module:hover &_module,
&_module:focus &_module {
...
}
}
- Every Block should maintain it's own Mediaqueries (later they'll get merged
by
grunt-combine-media-queries
Documentation)
Example
/* FILE: footer.less */
.footer {
...
}
/*
MEDIAQUERIES
*/
@media @media-screen-lt-large {
.footer {
...
}
}
- Mixins should always be used at the beginning of a Propery-Section, to make them overwriteable
/* BAD */
.module {
...
.my-mixin();
...
}
/* GOOD */
.module {
.my-mixin();
...
}
- CSS-Properties should be ordered by Alphabet (when possible)
/* BAD */
.module {
height: 1px;
position: absolute;
border: 1px solid red;
}
/* GOOD */
.module {
border: 1px solid red;
height: 1px;
position: absolute;
}
- Entry point
[..]/publicskin/resources/js/main.js
- All modules, which are not written by us, but used (installed by Bower or Hand) should be referenced in the
require.config()
Call. This approach makes it easy to reference them by name laterrequire( [ 'jq-cookie' ], function( jqCookie ) {} )
.
...
paths: {
'jq-cookie': 'bower/jquery.cookie/jquery.cookie',
'jquery': 'bower/jquery/dist/jquery',
'jquery-ui': 'bower/jquery-ui/ui'
'jwplayer': 'lib/jwplayer',
...
},
As soon, as a function becomes larger, it can make sense, to move the code to a Module, which gets only loaded, if it is needed. By default all Top-Level requires are included in the build.js
. To exclude certain Modules, see the configuration-section in the Gruntfile called requirejs
.
- as far as possible, try to divide CSS + JS. In the case of CSS-Classes this means:
<!-- BAD -->
<div class="article" />
<script type="text/javascript">
$( '.article' ).doSomething();
</script>
<!-- GOOD -->
<div class="article js--article" />
<script type="text/javascript">
$( '.js--article' ).doSomething();
</script>
With this approach its harder to break the JS-Code, by changing Names/ Structure of the Elements. Additionally the JS-Code should respect this and not assume a specific HTML-Structure. There can be performance implications, so the Idea is not static, but can be interpreted.
/* BAD */
var $this = $( this ),
$subThis = $this.children();
/* BETTER */
var $this = $( this ),
$subThis = $this.find( '.this_target' );
- All SVG-Icons are located in
[..]/publicskin/resources/image/
grunt-grunticon
(Documentation) compiles them into CSS-Sprites,grunt-svgmin
(Documentation) removes the bloat added by Illustrator- CSS-Naming Convention for icons is
<span class="icon icon--name" />
, where the--name
is the name of the SVG-File - Custom Selectors are defined in
publicskin/resources/image/icons.js
. This file is loaded by Grunt icons.less
holds a few adaptions (such as absolute positions, lists)