Frontend Library
@undkonsorten/frontend-library
This package is a block based (BEM) template basis based on Foundation for Sites by ZURB. It provides generalised and reusable features we are using in our Integration Library Frontend development.
Overview
- Installation
- Configuration
- Add a new feature
- Add a new Handlebars Helper
- Backwards compatibility
- Publish the package
- Changelog
Installation
cd project/frontend
yarn add @undkonsorten/frontend-library
Configuration
The following configuration is already done inside the Integration Library.
Once you have installed the package in your project, you need to do some configuration in order to use the library properly.
Note: The following instructions are based on the folder structure provided by the Foundation for Sites template.
config.yml
The config.yml
file, which provides several configuration presets, needs to be adjusted in order to use the Frontend Library resources
properly.
Note: You may need to create your own config.yml
file by copying the config.sample.yml
. Read more about it
here.
LIBRARY
variable
If not already exists, add the following configuration to your config.yml
file:
LIBRARY: "node_modules/@undkonsorten/frontend-library/"
This variable is not necessarily needed, but it allows you to easily access various resources out of the Frontend Library
by just appending the appropriate path to the LIBRARY
variable. For example: LIBRARY + "src/assets/scss"
Additional Sass path
Extend PATHS.sass
with the following line:
PATHS:
sass:
...
- "node_modules/@undkonsorten/frontend-library/src/assets/scss"
You need this to tell Sass the path of the additional Frontend Library resources in order to import and use them properly.
At this point, it's not possible to use the previously defined LIBRARY
variable since YAML doesn't have the possibility
to reuse some predefined configurations.
gulpfile.babel.js
The gulpfile.babel.js
provides all necessary Gulp tasks which are being used to build the Frontend out of the
desired features and contents which are developed for the project.
LIBRARY
variable declaration
If not already exists, add the previously defined LIBRARY
variable to the configuration load process:
const { ..., LIBRARY, ... } = loadConfig();
Note: The order of variable declaration needs to be equal to the variable definition inside your config.yml
file!
If you define LIBRARY
for example as second parameter in your config.yml
file, you need to declare it as the second
variable inside the const { ... }
declaration.
Extend pages
Task
Foundation for Sites makes use of the company-own file generator Panini. When generating the pages, Panini needs some resource paths in order to generate the appropriate files out of the given resources. Since the Frontend Library provides many features for the generation of pages, those resource paths need to be passed over.
Add the following to your pages
Task to include the Frontend Library source paths:
function pages() {
return gulp.src('src/pages/**/*.{html,hbs,handlebars}')
.pipe(panini({
root: 'src/pages/',
layouts: 'src/layouts/',
partials: ['src/blocks/', LIBRARY + 'src/blocks/'], // <- add the Frontend Library source here
helpers: ['src/helpers/', LIBRARY + 'src/helpers/'], // <- and here
data: 'src/data'
}))
.pipe(gulp.dest(PATHS.dist[buildTarget]));
}
Note that the source declaration for partials
and helpers
turned from string to array.
Extend watch
Task
In case you are working with a linked version of the Frontend Library, it might be helpful to watch those resources and run the appropriate Gulp Tasks again in case any changes were made.
Add the following to your watch
Task:
gulp.watch(LIBRARY + 'src/blocks/**/*.html').on('all', gulp.series(resetPages, pages, reload));
gulp.watch(LIBRARY + 'src/{assets,blocks}/**/*.scss').on('all', sass);
gulp.watch(LIBRARY + 'src/helpers/*.js').on('all', gulp.series(javascript, pages, reload));
The above code is only useful if you link your project with the Frontend Library using
yarn link path/to/frontend-library
Add a new feature
New features are always welcome :)
When you'd like to add a new feature, please think about the following:
- Is it a common feature which might be used again?
- Does the new feature solve a very own problem or is it a solution for an existing one?
- Which contents should be provided through the Frontend Library and which one's are too specific?
- Is it necessary to provide some settings to customize the feature? Please try to build the feature as general as possible to avoid unnecessary markup and runtime.
If you decided to build a new feature for the Frontend Library, please follow these rules:
General
- Directory of all features:
src/blocks
- Create a new directory for your new feature, e.g.
src/blocks/my-feature
- All contents of your new feature should be located inside this folder!
Handlebars
Handlebars is used to split features into partials which can then be loaded into several files. Each feature which is available within the Frontend Library describes a Handlebars Partial. Please take a look at the usage of Handlebars in Foundation if you aren't used to it.
Naming of files
We use BEM for the naming of files.
The file name of your main file equals the name of your previously created directory for your feature: src/blocks/my-feature/my-feature.html
When you create more files for your new feature, please use the following convention:
BEM component | File name |
---|---|
Block | my-feature.html |
Element | my-feature__element.html |
Modifier |
|
Try to use a minimum of files for your feature. Not every Element or Modifier needs an own file!
Define Settings
At the beginning of each file, please name the Handlebars Settings which are available for the related Partial:
Initialize default variables
After defining the settings, you can initialize the default variables. This is useful if you have defined a default
for any of your variables which makes use of an explicit value, such as 1
or yellow
.
Here's an example on how to initialize default values for variables:
This would initialize the variable my-variable
with the value default value
if it has not been initialized yet.
Usage
After creating the files, you can call them as Handlebars Partials from everywhere:
Sass
We use SCSS as stylesheet language, which is a modern variation of Sass.
Naming of files
For the naming of SCSS files, we also use BEM.
The file name of your main SCSS file again equals the directory name: src/blocks/my-feature/my-feature.scss
Please refer to the naming of Handlebars files in connection to BEM when creating additional SCSS files. It is useful to create separate files for specific Modifiers since you can easily import each of them when using the following described file structure.
File structure
Your SCSS files should contain the following structural elements:
- Import of necessary dependencies, e.g.
// Dependencies // @depends helpers/inverted <- you can also specify soft dependencies to files which are always included @import '../feature1/feature1.scss'; // <- the '.scss' extension is not necessarily needed @import '../feature2/feature2--modifier'; // <- it's also possible to set modifiers of features as dependency
- Definition of default values for each variable which is used only in this file with the name of your feature as prefix, e.g.
// Default Variables $my-feature-width: 100% !default; $my-feature-height: 150px !default;
- Mixin as content wrapper with the name of your file and
frontend-library-
prefix, e.g.Note: Please replace any non-alphanumerical character with a hpyhen (@mixin frontend-library-my-feature { // type your style definitions here... }
-
) and do not use BEM for mixin names.
Import SCSS files
The reason why the whole content of SCSS files is wrapped into a mixin is because of the separation between import and inclusion. The Frontend Library is used to provide several features, but you might not need all of them in your project. That's why all resources are provided through the inclusion of the Frontend Library itself, but to explicitly use one of them, you have to include the appropriate mixin.
In order to import each SCSS file from your feature within the Frontend Library, add the imports of your files to the
src/assets/scss/frontend-library.scss
file:
@import '../../blocks/my-feature/my-feature';
Attention: In case you defined some dependencies in your SCSS files, it's useful to import the dependencies first. Otherwise, please import alphabetically.
Include your feature's SCSS in your project
Inside your project, you can now include the SCSS of your new feature:
@include frontend-library-my-feature();
List of all features
A complete list and documentation of features can be found here.
Add a new Handlebars Helper
New Handlebars helpers are also very welcome since they allow us to use custom JavaScript within our Handlebars Partials. If you'd like to add a new Handlebars helper, please think about the following:
- Is it a useful and reusable Helper which is used in any feature?
- Does any Panini Helper already solve the problem you want to solve with your own Helper?
- Can Handlebars already solve the problem by it's own? Please take a look at it's documentation.
If you decide to add the new Helper, please follow these rules:
General
- Directory of all Helpers:
src/helpers
- Create a new file for your new Helper, e.g.
src/helpers/my-helper.js
File structure
Your Helpers JavaScript file should be structured this way:
- Documentation of your Helper, including the following elements:
- Problem and solution
- Description
- Usage (Handlebars Syntax)
- Function declaration using Node's module export:
module.exports = function() { ... }
- Function parameters, depending on what problem you want to solve:
Parameter | Usage | Example |
---|---|---|
Named parameters, e.g. function(width, height)
|
Solve a solution depending on several defined settings | {{my-helper 10 20}} |
Non-named parameters, e.g. function(options)
|
Provide multiple settings without explicitly defining them | {{my-helper width=10 height=20 depth=30}} |
Context parameter: function(..., context)
|
Access content area provided within the Helpers call | {{#my-helper}}I like that.{{else}}I really don't like it.{{/my-helper}} |
You can certainly combine all variants.
Backwards compatibility
The library has several methods to handle backwards compatibility.
SCSS
In our SCSS files we use a version-dependent system for backwards compatibility. As the name points out, the system relies on the version of the package.
The current version is defined in the frontend-library.scss
file:
$appVersion: "1.2.0";
The file _dev.scss
provides a mixin to handle deprecated variables, mixins and
functions:
@mixin deprecate($nameOfDeprecated, $nameOfCurrent, $versionOfDeprecation, $versionOfRemoval, $type: variable) {}
In case a variable/mixin/function is marked as deprecated
, the mixin should be used to inform the developer about the
deprecation. For this, add the following to the appropriate file where the deprecated variable/mixin/function is declared:
// For variables
@if global-variable-exists(old-variable-name) {
$new-variable-name: $old-variable-name;
@include deprecate(old-variable-name, new-variable-name, "1.2.0", "2.0.0");
}
// For variables without replacement (just set the second parameter to "null")
@if global-variable-exists(old-variable-name) {
@include deprecate(old-variable-name, null, "1.2.0", "2.0.0");
}
// For mixins
@mixin old-mixin-name {
@include new-mixin-name();
@include deprecate(old-mixin-name, new-mixin-name, "2.0.0", "3.0.0", mixin);
}
// For mixins without replacement (just set the second parameter to "null")
@mixin old-mixin-name {
@include deprecate(old-mixin-name, null, "2.0.0", "3.0.0", mixin);
}
Apparently, there is no useful method to use this in deprecated functions. So please make sure to handle useful backwards compatibility for functions and try to inform your users about the deprecation as well.
Handlebars
Within Handlebars, it's also possible to use backwards compatibility. Currently, this is only possible for variables and
partials. The Handlebars Helper deprecate
provides a deprecation system to handle deprecated
variables and partials:
module.exports = (name, context) => {}
It can be used as follows:
This would generate a warning in your console if the variable old-var
is set and marked as deprecated
. The same
behaviour can be achieved for partials. For this, use the type partial
as argument instead.
When raising deprecation notices for variables, it's also possible to set the value of the replacing variable with
the value of the deprecated variable in the current context. For this, pass the parameter replace
with value 1
:
Overview of accepted parameters:
Parameter | Description | Accepted values |
---|---|---|
<initial> | Name of the deprecated resource | <string> |
replacement |
Name of the resource which replaces the deprecated one | <string> |
since |
Version of deprecation | <float> |
remove |
Version of removal | <float> |
type |
Resource type |
var , partial
|
replace |
Set new variable with value of deprecated variable | <boolean> |
Note: When using both default variables and backwards compatibility, make sure to keep the order of definition as follows:
- Default variables
- Backwards compatibility
Publish the package
When you made all necessary changes to the Frontend Library, you can publish the updated package to npm. For this, you need to update the version number and then push the updated package.
Please remember to log all notable changes to the Changelog.
Semantic Versioning
If you want to push the updated package to npm, you need to set a higher version number first. According to Semantic Versioning, there exist three types of version numbers:
- Major version:
1.0.0
->2.0.0
(breaking changes) - Minor version:
1.0.0
->1.1.0
(new functionality with backwards compatibility) - Patch version:
1.0.0
->1.0.1
(bug fixes)
Please make sure to follow the rules of Semantic Versioning when updating the package.
Note: Your Git working directory needs to be clean in order to increase the version number.
Publish using bash script
You can use a bash script to publish your changes.
./sbin/publish.sh <update_type> [-m MESSAGE] [-p] [-f]
Replace <update_type>
with one of the version number types described above (major
, minor
or patch
).
Parameter | Description |
---|---|
-m MESSAGE |
Release message (will be used as commit message as well) |
-p |
Push Git version commit (otherwise, this needs to be done manually) |
-f |
Force publishing the package to npm (this skips the prompt asking to publish the package) |
Do it manually
Alternatively, the publish process can also be done manually.
Set version number
Depending on which type of Semantic Versioning you have chosen for the update, you may use one of the following statements
(%s
will be replaced with the new version number):
npm version major -m "New template structure added in %s"
npm version minor -m "New feature `my-feature` added in %s"
npm version patch -m "Fixed bug #12345 in %s"
When running npm version
, a new Git commit containing the new version number as commit message will be added which
updates the version number inside the package.json
file. It also creates a new tag pointing to the version commit.
Update new version number for SCSS
Now we have to update the version number variable in SCSS with the new version number we have previously generated using
npm version
.
For this, update the variable $appVersion
at the top of the src/assets/scss/frontend-library.scss
file:
$appVersion: "1.1.0";
After updating the version number for SCSS, you have to commit this change and amend it to the previously generated commit:
git commit --all --amend
git tag -f $(git tag | tail -n1)
Now push the version commit and tag:
git push --follow-tags
If the above command does not push both your commits and tags, you need to push them separately:
git push && git push --tags
Publish
Once you have changed the version number, you can publish the new version to the npm registry.
npm publish
Changelog
For detailed changes across each version take a look at the Changelog.