Frontastic Coding Guide

Below are the common standards for Frontastic development. These guidelines are mandatory for development inside Frontastic and are highly recommended for Project development. If you have a valid reason for going against the guidelines, please document these reasons and let us know.

In this article:

Automatic static code analysis can be triggered using the below command in any module:

ant test-static

Be sure to run this command frequently. Some rules will even let the Continuous Integration build fail, so you'll notice then at the latest.


GIT Workflow

Frontastic follows a "Master based development" flow (originally known as "trunk based development"). This means, branches are generally discouraged. All code should go directly into Master. This requires each push (ideally each commit) to leave the code base fully functional.

Commit Guidelines
  • Pull before you push
  • Rebase unpushed changes in favor of merge (set pull.rebase globally to true)
  • Structure your work in logical steps and commit parts of your work together which deal with a common purpose
  • Frequent, smaller commits are preferred over large batches of work
  • Push frequently, but always ensure a working state in master
Commit Message Guidelines
  • Every commit message consists of at least a subject line explaining the change in a few meaningful words
  • Limit the subject line to 50 characters (soft limit) respectively 80 characters (hard limit)
  • Capitalize the subject line
  • Use past tense in the subject (Fixed Schema to complete defaults instead of Fixes Schema to complete defaults)
  • If you're working on a ticket, prefix the subject by the ticket number using a # (for example, #4223 Implemented model for product types)
  • Add a body to your commit to explain the reasons for your change if you feel it's necessary (for example, removing a feature, changing a behavior for certain reasons, etc.)
  • Divide the subject from the body using a blank line
  • Use of Markdown style elements in the body is permitted (for example, lists)
Master Based Development Guidelines
  • Run (all/component) tests before pushing ($ ant test)
  • Use an iterative development approach (start with the smallest functional feature possible and extend it subsequently)
  • Create tests for all new code to ensure it's basically working (no need for full code-coverage or similar)
  • Implement code without integrating it directly into the app before it's functional (use tests!)
  • Deactivate the effect of your code using a feature-flag if it could disturb others while being under development
  • If you're unsure about changing existing code and it doesn't have (enough) tests: create tests first
  • Always test the frontend parts that your change affects in your development VM/Container before pushing

If you're unsure if a specific part of your code is the right way of doing it, feel free to create a minimal branch for that specific piece of code and let us know.


Programming Language Crossing Coding Style

Frontastic encourages Clean Code as described in Robert Martin's book.

Most importantly, the following rules should by applied to any kind of code:

  • Stick to the patterns you find in existing code
  • If you find code places that can be optimized for cleanness, go ahead and optimize them (Boy Scout rules)
  • Use meaningful names for all code entities (classes, methods, fields, variables, files, ...)
  • Avoid and encapsulate side-effects
  • Use exceptions for errors
  • Avoid comments that repeat code
  • Add comments where you do something unusual
  • Keep comments short and to the point
  • Frequently run the code analysis tools available ($ ant test)
Docs for CSS structure
Principles

It's always hard to organize CSS really well so we use the principle of ITCSS (Inverted Triangle CSS) to separate our Folder Structure into different layers.

Frontastic CSS Design Principle Triangle

Folder Structure

Be sure to follow the ITCSS structure and a number should be given to each folder.

Folder Name and Number Description Prefix File Example Code Example
00-settings Global settings of the project. The output is not directly in CSS N/A
01-tools Functions used globally, the output is not directly in CSS N/A _tools.box-shadow.scss
@mixin box-shadow() {
			box-shadow: 0 0 5px rgba(0,0,0,.3);
		}<br>
		
02-generic Mostly resets or normalizes HTML elements. The first layer which generates some CSS N/A _generic.reset.scss
body {
  box-sizing: border-box;
}
		
03-elements Redefinition default styling from the browser N/A _elements.input.scss
input {
  border: 1px solid $color-gray-light;
}<br>
		
04-objects Composition of undecorated design patterns like media object, lists etc. o- _objects.media.scss
.o-media {
  display: flex;
  flex-direction: row;
}
		
05-components The majority of work happens here, contains specific UI Components as well as some local component configurations here. c- _components.button.scss
.c-button {}
		
06-utilities This layer has the ability to overwrite anything which goes before the triangle. u- _utilities.text.scss
.u-danger {
  color: red;
}
		

Importing files
  • Don't use _ before the filename
  • Separate each block with an own import statement
  • Separate each file with comma
  • Don't use .scss ending
@import "00-settings/settings.colors",
     "00-settings/settings.layout";

@import "01-tools/tools.border",
     "01-tools/tools.get-color";
File Structure

A file can have different sections. The first section is the configuration area where you can define component-based settings. Then you give the component the style.

Please use childs and states in the block. Under the block you can describe the variations.

// Config
$button-border-color: blue;

// Component
/// @group Buttons
     .c-button {
	cursor: pointer;
	display: inline-block:
	padding: get-spacing-squished(m);
	vertical-align: top;
	-webkit-appearance: none;
	-moz-appearance: none;
	@include border(transparent);
	background: get-color-brand(primary);
	color: get-color(unique, white);
	line-height: 1;
	text-align: center;

     &:hover {
     }

     &__child {
	color: red;
     }

// States
     &.is-active {
	color: blue
     }
}

/// Modifiers
.c-button--ghost {}
.c-button--boss {}
BEM

Block, Element, Modifier. Always use this naming convention when you name your classes.

// Block
.c-accordion {

     // Trigger
     &__trigger {}

     // Modifier
     &--boss {}
}
Prefix for Separation
// Object
.o-grid {}

// Component
.c-accordion {}

// Utility
.u-hide {}
Using T-Shirt Sizes

If you have hierarchical values, then use the different steps as T-shirt sizes.

$font-size: (
     xs: .6rem,
     s: .8rem,
     m: 1rem,
     l: 1.4rem,
     xl: 1.8rem
) !default;
JS Hooks

Sometimes we need a class for only JS Hooks. It's important that JavaScript doesn't give the elements a style. For all JS Hooks Classes we use the `js' Prefix.

<div class="c-accordion js-accordion"></div>
State Handling

We separate States from the block. You can remove or add these classes with JS for State handling. Mostly these classes have a prefix like is-.

.c-button {
     &.is-active {
     }
}
Categorizing CSS Rules

We use the SMACSS approach and categorize our CSS Rules/Properties in different sections. Please structure the properties in each block alphabetically.

  • Box
  • Border
  • Background
  • Font
  • Other
c-button {
     // Box
     padding: 24px;
     position: relative;
     top: 0;

     // Border
     border: 1px solid #000;

     // Background
     background: red;

     // Font
     color: #fff;
     line-height: 1;

     // Other
     transform: scale(1);
}
Separate Colors

A system with three colors will help to scale up the architecture and we use the groups below:

Palette Colors

Every color which you want to use in the project should be added into this palette.

$color-palette: (
     unique: (
	white: #fff,
	black: #000
     ),
     orange: (
	base: #FFCE18
     ),
     gray: (
	light: #C1C2C5,
	base: #98999F,
     )
) !default;
Brand Colors

These colors are only for the brand specific colors and the key colors for the theme. In this level we need a more generic approach, so the naming is a bit different.

$color-brand: (
     primary: get-color(blue),
     primaryGradation: get-color(blue, light),
     secondary: get-color(orange),
     secondaryGradation: get-color(orange, light)
) !default;

Brightness is available in lighter, light, base, dark and darker.

Layout Colors

Mostly used for areas like boxes with a background, promoboxes or whisperboxes.

$color-layout: (
     ghost: get-color(unique, white),
     spot: get-color(blue)
) !default;
Semantic Colors

These colors define the semantic of a component, element, etc. For example, hints, success or error states. Use meaningful and unique colors for feedback like success, warning, danger, etc.

$color-semantic: (
     success: #98C674,
     successGradation: #F0FAEA,
     error: #E07676,
     errorGradation: #FAEAEA
) !default;
Avoid !important and IDs

Please don't use !important or IDs for styling as we prefer classes. An ID should only be used for JS. The only exception for !important is in utilities where it's possible to use it.

.u-hide {
     display: none !important;
}
Line-Breaks, Whitespaces (One Tab = Two Whitespaces)
.c-button {
     margin: 12px;
     padding: 24px;
}
Use the Function to Get the Color
// DO: use the function
     .c-button {
	background: get-color-brand(primary);
     }

// DON'T: use hex value of the color in the component
     .c-button {
	background: #000;
     }
Handling Spacing

CSS use properties like margin, padding and absolute positioning to separate objects. We think that Spacing should have its own concept like the color section. Sometimes you need padding for a box, a space to the item left or a distance to the next row. So we should separate these into different categories.

Frontastic Coding Guidelines Handling Spacing

Separate in these categories:

  • Inset - A inset spacing offers indent spacing like boxes or a photo frame on a wall.
  • Frontastic Coding Guidelines Inset Definition
  • Inline - We put elements in a row like a list of chips. So we need a space between these elements.
  • Frontastic Coding Guidelines Inline Definition
  • Stack - In the general case you scroll vertically through the user interface. So we stack elements like a heading on a data table.
  • Frontastic Coding Guidelines Stack Definition
  • Squish - The squish inset reduces the space top and bottom. It's mostly used in buttons and listitems.
  • Frontastic Coding Guidelines Squish Definition Stretch - The stretch inset reduces the space left and right. It's mostly used in  textboxes, textareas and other form elements.

Frontastic Coding Guidelines Stretch Definition

Scaling

It always helps to use a value system for the creation of spacing. So we use a non-linear System. Starting from the base, we go in both directions to stops smaller (16, 8, 4, 2) and larger (16, 32, 64) on the scale.

Frontastic Coding Guidelines Scaling

Handling Indices

Please do not use an index directly as a property. You must add it to the map and use the get-index() function.

// DO: using a map for a good overview
     $indices: (
	navbar: 10,
	header: 20
     ) !default;

     .c-header {
	z-index: get-index(header);
     }

// DON'T: z-index in the component
     .c-header {
	z-index: 20;
     }
Alphabetical Order
.c-button {
     left: 0;
     margin: 0;
     padding: 0;
     position: relative;
 
     color: green;
     font-size: 21px;
     line-height: 1;
}
Don't Use Very Generic or Specific Class Names
// DO: Super Light Button
     .c-button--ghost {
     }
// DON'T: So specific
     .c-button--red {
     }
// DON'T: It can contain everything
     .c-button--alternative {
     }
Don't Use ShortNames
// DO: someone will know what do you mean
     $font-weight-light: 300;
// DON'T: a short name
     $fw-light: 300;
SCSS Only With a Map

If you write a SCSS Map, then you must create a function to get a value from it the easy way.

// Map
     $indices: (
	navbar: 10,
	header: 20
     ) !default;
// Function @function
     get-index($key) {
	@if map-has-key($indices, $key) {
	@return map-get($indices, $key);
     }
	@warn "The key #{$key} is not in the map ’$indices’";
	@return null;
     }
Atomic Design

We only use this principle for categorizing all kinds of components in the frontend: It doesn't reflect our CSS structure.

atoms/
molecules/ 
organisms/ 
templates/ 
objects/
Storybook
Design Tokens

https://medium.com/@didoo/how-to-manage-your-design-tokens-with-style-dictionary-98c795b938aa

Readable Articles
https://medium.com/codyhouse/line-height-crop-a-simple-css-formula-to-remove-top-space-from-your-text-9c3de06d7c6f

PHP Coding Conventions

The following conventions apply to PHP / backend code.

Basics

The following PSR (PHP Standardization Request) must be followed:

Besides that we are using Symfony 4.1 in all of our PHP based components and attempt to follow its rules.

General
  • No mixed data type hints or mixed return type hints
  • Tests for all important, critical or complex code, especially in services
  • Session mustn't contain any data but the logged in users ID
    • Never use Session beyond the controller
  • Always use the Request object to access request data (Header, Body, Query String, Files, …)
    • Only use request objects in controllers
  • Don't suppress any error from PHP during runtime (@)
  • Don't use static (except for factory methods in data objects)
    • NEVER use static class properties
Functions and Methods
  • Keep functions small and modular
  • Function and methods have a limit of 50 LOC
Classes
  • Properties first
  • Try to limit the number of public methods on services to a maximum of 10
  • Helper methods should be below the public method using them
  • Classes shouldn't exceed 256 LOC
Naming
  • Don't use types in names
  • Keep abbreviations to a minimum
  • Always use English naming
    • Try to use variable names relating to a common understanding of the domain
    • A variable name should describe what is contained in the variable
    • Be nice to the reader and your co-worker
Use Exceptions
  • Extend from component (Base Exception)
  • Always throw exception in case of errors, don't return null or false
  • Handle exceptions sensibly at latest in controllers
    • Display sensible error messages depending on exception type
    • Log technical failures, alert operations
  • Try to avoid returning false in case of actual Exceptions
  • Only in the case of Validation is it OK to return false
Gateways
  • Get-Methods (like getById) are allowed to throw an Exception on not found
  • Find-Methods aren't allowed to throw Exceptions, null should be returned
  • By default use DQL. If a query needs optimization or something is not supported write raw SQL
  • No Business Logic – simple checks or mapping is OK
  • Save/Read data from/to database or any other data source
  • Return and receive always data objects. Primitive types in documented edge-cases
  • Services depend on Gateways (interfaces)
DataObjects
  • Use them for all data -- never StdClass
  • Never use arrays as data structures
  • Data objects must not aggregate "active" dependencies (gateways, services)
  • Only logic modeling eternal truth
  • Avoid to create multiple DataObjects with same name
  • Don't use getter / setters: Use public properties and direct property access
Service
  • Max 4 dependencies
    • Technical constraints like logging and caching should be moved into Decorators
  • All dependencies are injected
  • The business logic should be contained here
  • No dependencies on externals – each external class should be wrapped behind a facade (Symfony, DB (Gateways), Webservices, …)
    • Not even the Request object, but DataObjects created from the request data
  • Respect Law Of Demeter – only work with direct dependencies
Controller
  • Catch Exceptions
  • Check Permissions
  • Convert incoming data into object
  • No (Business) Logic (only validation or Simple authorization like "is logged in")
  • Use Request and Response objects
MySQL
  • Write keywords in statements in uppercase
  • No JOINs without condition
  • No implicit JOINs
  • All table names and column names are in singular
  • All columns in a table (user) are prefixed with the unique table prefix (u_) -- especially also the id (u_id)
    • A foreign key reference will use the column name from the referenced table (comment:u_id)

‹ Back to Article List

Next Article ›

Commands List

Still need help? Contact Us Contact Us