Lea Bialachowski and Matthew Neil
Repeated UI patterns extracted into core modules, approach to problems, layout used, spacing top or bottom for panel elements
List and link list, tabs, gradients
Quicker than web essentials, easy to use, more configurable, does more things that are useful (image compression, concat without bundling), doesn't sudenly break or crash
/// <vs SolutionOpened='images' />
module.exports = function (grunt) {
// load nom tasks from package.json
require('load-grunt-tasks')(grunt);
// show timing of build tasks
require('time-grunt')(grunt);
// just in time load tasks / plugins faster
require('jit-grunt')(grunt)({
customTasksDir: 'grunt_tasks'
});
// global folder configuration
var options = {
config: {
src: "grunt_tasks/*.js"
},
paths: {
localDir: 'C:/inetpub/wwwroot/BurgesSalmon/Website',
localHost: 'BurgesSalmon/',
// css paths
cssSrc: 'src/css',
cssDist: 'dist/css',
// scripts paths
jsSrc: 'src/scripts',
jsDist: 'dist/scripts',
jsTest: 'src/scripts/specs',
// image paths
imgSrc: 'src/images',
imgDist: 'dist/images',
// Sitecore modules
wffmSrc: 'src/css/modules/wffm',
wffmDist: 'sitecore modules/Shell/Web Forms for Marketers/Themes',
wffmCssFileName: 'burges-salmon'
}
};
// Load grunt configurations automatically
var configs = require('load-grunt-configs')(grunt, options);
// Define the configuration for all the tasks
grunt.initConfig(configs);
// Default task(s).
grunt.registerTask('default', ['watch']);
grunt.registerTask('styles', ['less:main', 'postcss:dist', 'cssmin:main', 'newer:imagemin', 'copy:styles']);
grunt.registerTask('scripts', ['newer:jshint:all', 'concat', 'uglify', 'copy:scripts']);
grunt.registerTask('images', ['newer:imagemin', 'newer:svgmin', 'copy:images']);
grunt.registerTask('test', ['jasmine']);
grunt.registerTask('wffm', ['less:wffm', 'postcss:wffm', 'cssmin:wffm', 'copy:wffm']);
// js debug
grunt.registerTask('jsdebug', ['concat', 'uglify', 'copy:scripts']);
grunt.registerTask('prod', [
'jshint:all',
'concat',
'uglify',
'less:main',
'postcss:dist',
'cssmin:main',
'less:wffm',
'postcss:wffm',
'cssmin:wffm',
'copy:wffm',
'imagemin',
'copy:prod'
]);
};
@import (reference) "../../core/_breakpoints.less";
@import (reference) "../../core/_variables.less";
.profile-card {
box-sizing: border-box;
color: @dark-grey;
margin-top: @division-medium;
width: 100%;
display: flex;
background-size: 100% 105px;
position: relative;
top: 0;
transition: top 0.25s @cubic-1, box-shadow 0.3s @cubic-1;
box-shadow: 0px 3px 10px rgba( 0, 0, 0, 0);
&__content-container {
width: 100%;
background: linear-gradient( @grey-100, @grey-100) no-repeat 0 105px;
position: relative;
align-self: stretch;
display: flex;
&:before {
content: '';
transition: all 1s;
opacity: 1;
background: @grey-150 linear-gradient( @grey-100, @grey-100) no-repeat 0 105px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 4;
}
&:hover {
&:before {
opacity: 0;
}
.profile-card__name:after {
background: @bs-magenta;
}
}
}
&__content {
position: relative;
z-index: 5;
padding: 30px 15px 90px 15px;
align-self: stretch;
width: 100%;
}
&__name {
margin-bottom: 15px;
}
&__title {
font-size: 75%;
display: block;
}
&__list-label {
padding-top: 10px;
display: block;
text-align: left;
padding-left: 17px;
font-size: 105%;
}
&__list {
text-align: left;
padding-top: 0;
@media @tablet {
li:nth-child(n+4){
display: none;
}
}
}
&__button-container {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
}
}Variables, darken, lighten
@import (reference) "../../core/_breakpoints.less";
@import (reference) "../../core/_grid.less";
@import (reference) "../../core/_utilities.less";
// Developer notes:
// The wffm selectors have been extended to the _forms.less file to reduce duplicated styles
// Required wffm selectors are then output to the sitecore modules folder...
// sitecore modules\Shell\Web Forms for Marketers\Themes
.scfForm {
.page-editor &,
.page-preview & {
font-family: 'Frutiger Pro 45 Light';
}
.scfSectionBorderAsFieldSet {
&:extend(fieldset);
text-align: left;
}
.scfSectionLegend {
&:extend(legend);
.page-editor &,
.page-preview & {
font-family: inherit;
font-weight: inherit;
font-size: inherit;
color: inherit;
}
}
.scfTitleBorder {
.page-editor &,
.page-preview & {
// Used to overide the default styles injected via page editor
text-align: center;
font-family: inherit;
}
}
.scfDropListLabel,
.scfEmailLabel,
.scfMultipleLineTextLabel,
.scfSingleLineTextLabel,
.scfPasswordLabel,
.scfNumberLabel,
.scfDatePickerLabel,
.scfDateLabel,
.scfRadioButtonListLabel,
.scfListBoxLabel,
.scfFileUploadLabel,
.scfDateSelectorLabel,
.scfCreditCardLabel,
.scfConfirmPasswordLabel,
.scfCaptchaLabel,
.scfTelephoneLabel,
.scfSmsTelephoneLabel {
&:extend(label);
order: 0;
.page-editor &,
.page-preview & {
// Used to overide the default styles injected via page editor
&:extend(label);
order: 0;
width: auto;
}
}
.scfCheckBoxListLabel {
&:extend(label);
order: 0;
}
.ui-icon-datepicker {
display: none;
}
.scfCheckboxBorder {
display: flex;
flex-wrap: wrap;
> .scfCheckBoxListGeneralPanel {
width: auto;
}
.scfCheckboxUsefulInfo {
+ .scfRequired {
order: 4;
}
}
}
.scfCheckBoxListGeneralPanel {
order: 2;
width: 100%;
}
.scfRadioButtonList,
.scfCheckBoxList {
label {
margin: @division-small / 2 0 0;
}
}
.scfSingleLineTextBorder,
.scfDropListBorder,
.scfEmailBorder,
.scfMultipleLineTextBorder,
.scfSingleLineTextBorder,
.scfPasswordBorder,
.scfNumberBorder,
.scfDateBorder,
.scfRadioButtonListBorder,
.scfListBoxBorder,
.scfCheckBoxListBorder,
.scfFileUploadBorder,
.scfDateSelectorBorder,
.scfCreditCardBorder,
.scfConfirmPasswordBorder,
.scfCaptchaBorder,
.scfTelephoneBorder,
.scfSmsTelephoneBorder {
box-sizing:border-box;
display: flex;
flex-wrap: wrap;
width: 100%;
padding:0 @division-small/2;
}
.scfEmailGeneralPanel,
.scfMultipleLineGeneralPanel,
.scfSingleLineGeneralPanel,
.scfPasswordGeneralPanel,
.scfNumberGeneralPanel,
.scfDateGeneralPanel,
.scfRadioButtonListGeneralPanel,
.scfFileUploadGeneralPanel,
.scfDateSelectorGeneralPanel,
.scfCreditCardGeneralPanel,
.scfConfirmPasswordGeneralPanel,
.scfCaptchaGeneralPanel,
.scfTelephoneGeneralPanel,
.scfSmsTelephoneGeneralPanel,
.scfDropListGeneralPanel,
.scfListBoxGeneralPanel,
.scfDatePickerGeneralPanel {
text-align: left;
order: 3;
width: 100%;
}
.scfDateSelectorGeneralPanel {
label {
}
.scfDateSelectorShortLabelDay,
.scfDateSelectorShortLabelMonth,
.scfDateSelectorShortLabelYear,
.scfDateSelectorYear,
.scfDateSelectorMonth,
.scfDateSelectorDay {
display: inline-block;
width: calc(100% / 3);
border: 5px solid @white;
box-sizing: border-box;
.page-editor &,
.page-preview & {
color: inherit;
font-size: inherit;
float: none;
line-height: inherit;
padding: 0;
}
}
.scfDateSelectorYear,
.scfDateSelectorMonth,
.scfDateSelectorDay {
min-height: 60px;
}
}
.scfListBox,
.scfDropList {
}
select {
&:extend(select);
}
textarea {
&:extend(textarea);
}
input {
&[type="text"],
&[type="date"],
&[type="email"],
&[type="month"],
&[type="number"],
&[type="password"],
&[type="tel"],
&[type="time"],
&[type="week"],
&[type="url"] {
&:extend(input[type="text"]);
}
}
// form validation
.scfValidatorRequired,
.scfRequired {
color: @dark-grey;
order: 1;
margin: 25px 0 0 10px;
}
.scfValidator {
}
.scfIntroBorder {
clear: both;
.page-editor &,
.page-preview & {
text-align: center;
}
}
.scfFooterBorder {
text-align: center;
}
.scfError,
.scfValidationSummary,
.scfValidatorRequired,
.scfValidator {
&:extend(.input-error);
}
.scfValidationSummary {
border: 2px solid @bs-red;
background: fade(@bs-red, 10%);
margin-top: @division-small;
ul {
padding: @division-small;
li {
list-style: none;
&:before {
display: none;
}
}
}
}
.scfCreditCardTextUsefulInfo,
.scfConfirmPasswordUsefulInfo,
.scfDateSelectorUsefulInfo,
.scfCaptchaUsefulInfo,
.scfTelephoneUsefulInfo,
.scfSmsTelephoneUsefulInfo,
.scfPasswordUsefulInfo,
.scfSingleLineTextUsefulInfo,
.scfMultipleLineTextUsefulInfo,
.scfEmailUsefulInfo,
.scfNumberUsefulInfo,
.scfDatePickerUsefulInfo,
.scfDropListUsefulInfo,
.scfListBoxUsefulInfo,
.scfRadioButtonListUsefulInfo,
.scfCheckBoxListUsefulInfo,
.scfFileUploadUsefulInfo,
.scfCheckboxUsefulInfo {
display: block;
margin: 5px 0 0;
color: fade(@bs-black, 50%);
text-align: left;
order: 5;
width: 100%;
}
.scfSubmitButton:extend(.btn) {
// Duplicated style as you cannot extend nested &--selector
height: 60px;
line-height: 62px;
background: @bs-magenta url(/dist/images/icons/Icons_Arrow_White.svg) no-repeat center right 20px;
color: @white;
background-size: 10px;
&:hover:extend(.btn:hover) {
// this extends to the _forms.less file on compile
}
}
.scfSubmitButtonBorder {
.page-editor &,
.page-preview & {
text-align: center;
}
}
.scfSectionContent {
display: flex;
flex-wrap: wrap;
}
/* BASE SITECORE CLASSES */
/** Sitecore added classes applied to form elements **/
// form 50/50 split columns sitecore set in webforms
.halfAvailableWidth,
.thirdAvailableWidth {
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
float: none;
padding: 0 @division-small/2;
width: 100%;
}
.halfAvailableWidth {
@media @tablet {
width: 50%;
}
}
.thirdAvailableWidth {
@media @tablet {
width: calc(100%/3);
}
}
/* CUSTOM KAGOOL CLASSES */
.two-column,
.three-column {
tbody {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
tr {
box-sizing: border-box;
width: 100%;
}
}
.two-column {
tr {
@media @tablet {
width: 50%;
}
}
}
.three-column {
tr {
@media @tablet {
width: calc(100% / 3);
}
}
}
}Extending and avoiding mixins
@import (reference) "_variables.less";
@import (reference) "_breakpoints.less";
@import (reference) "_typography.less";
// Mixin used as range slider requires specific browser selectors :(
.range-thumb() {
background: @bs-magenta;
border-radius: 20px;
width: 20px;
height: 20px;
cursor: pointer;
}
/* BASE FORM */
fieldset {
border:none;
padding:0;
margin:@division-small 0 0;
}
legend {
.short-text();
text-align:center;
width:100%;
&:after {
&:extend(h2:after);
}
}
label {
display:block;
text-align:left;
font-size: @base-font-size * @p;
margin:30px 0 0;
line-height:1;
}
input {
&[type="text"],
&[type="email"],
&[type="password"],
&[type="date"],
&[type="month"],
&[type="number"],
&[type="datetime"],
&[type="datetime-local"],
&[type="tel"],
&[type="time"],
&[type="week"],
&[type="url"],
&[type="search"] {
box-sizing: border-box;
border-radius:0;
border: 2px solid @grey-100;
background: @grey-100;
color:@grey-80;
min-height: 50px;
margin: @division-small 0 0;
padding: 10px @division-small;
width: 100%;
text-align: left;
-webkit-appearance: none; /* iOS input madness */
&:focus {
-webkit-appearance: none; /* iOS input madness */
outline: none; /* remove chrome blue outline */
}
&:required {
border-bottom-color: @bs-red;
}
}
&[type="datetime"],
&[type="datetime-local"] {
padding: 10px 5px 10px @division-small;
}
&[type="file"] {
margin: @division-small 0 0;
}
// RANGE ALL BROWSERS
&[type="range"] {
width: 100%;
-webkit-appearance: none;
&:focus {
outline: none;
}
/* WEBKIT */
&::-webkit-slider-thumb {
-webkit-appearance: none;
margin-top: -5px;
.range-thumb();
}
&::-webkit-slider-runnable-track {
animation: 0.2s;
background: @grey-100;
cursor: pointer;
height: 8px;
width: 100%;
transition: background 0.25s @cubic-1;
}
/* FIREFOX */
&::-moz-range-thumb {
border: none;
margin-top: -5px;
.range-thumb();
}
&::-moz-range-track {
animation: 0.2s;
background: @grey-100;
cursor: pointer;
height: 8px;
width: 100%;
transition: background 0.25s @cubic-1;
}
/* IE */
&::-ms-track {
animate: 0.2s;
width: 100%;
height: 8px;
cursor: pointer;
background: transparent;
border-color: transparent;
border-width: 8px 0;
color: transparent;
}
&::-ms-fill-upper,
&::-ms-fill-lower,
&:focus::-ms-fill-upper,
&:focus::-ms-fill-lower {
background: @grey-100;
}
&::-ms-thumb {
border: none;
.range-thumb();
}
}
&[type="checkbox"] {
display: none;
+ label {
position: relative;
}
&:checked {
+ label:before {
content: '\2713';
color: @bs-green;
}
}
+ label:before {
box-sizing: border-box;
content: '';
cursor: pointer;
width: 20px;
height: 20px;
border: 1px solid @dark-grey;
display: inline-block;
vertical-align: middle;
margin-right: 10px;
text-align: center;
}
}
&[type="radio"] {
display: none;
+ label {
position: relative;
}
&:checked {
+ label:before {
background: @dark-grey;
}
}
+ label:before {
content: '';
width: 8px;
height: 8px;
display: inline-block;
vertical-align: middle;
margin: 0 16px 0 6px;
text-align: center;
border-radius: 50%;
background: @white;
box-shadow: 0 0 0 5px @white, 0 0 0 6px @dark-grey;
}
}
&[type="search"] {
background-image: url(/dist/images/icons/Icons_Search.svg);
background-repeat: no-repeat;
background-position: center right 10px;
background-size: 20px;
}
&[type="submit"],
&[type="reset"] {
margin-left: @gutter-width-px;
margin-right: @gutter-width-px;
}
}
textarea {
&:extend(input[type="text"]);
resize: vertical;
&:focus {
-webkit-appearance: none; /* iOS input madness */
outline: none; /* remove chrome blue outline */
}
}
select {
&:extend(input[type="text"]);
-webkit-appearance: none;
-moz-appearance: none;
background-image: url(/dist/images/icons/Icons_Select_Arrow.svg);
background-repeat: no-repeat;
background-position: center right 10px;
background-size: 12px;
&[size] {
background-image:none;
}
&::-ms-expand {
border: none;
color: transparent;
background: transparent url(/dist/images/icons/Icons_Select_Arrow.svg) no-repeat center;
background-size: 12px;
}
&:focus {
-webkit-appearance: none; /* iOS input madness */
outline: none; /* remove chrome blue outline */
}
}
/* RADIO & CHECKBOX LIST */
.radio-list,
.checkbox-list {
ul {
padding: 0;
}
li {
&:before {
display:none;
}
}
}
/* VALIDATION */
.input-error {
color:@bs-red;
}
/* PLACEHOLDER */
::-webkit-input-placeholder,
::-moz-placeholder,
:-ms-input-placeholder,
input:-moz-placeholder
{
color: fade(@bs-black, 50%);
}
.disabled {
opacity: 0.25;
pointer-events: none;
}_web-forms.less
_forms.less
Would have normally had to override the webform styles or create a less file in the sitecore modules folder
module.exports = {
// Less
main: {
options: {
sourceMap: true,
paths: ['<%= paths.cssSrc %>'] // scans for @import
},
files: [{
expand: true, // Enable dynamic expansion.
cwd: '<%= paths.cssSrc %>', // Src matches are relative to this path.
src: ['**/main.less', '**/print.less'], // Actual pattern(s) to match.
dest: '<%= paths.cssDist %>', // Destination path prefix.
ext: '.css', // Dest filepaths will have this extension.
}],
},
wffm: {
options: {
sourceMap: false,
paths: ['<%= paths.scModule %>']
},
files: {
'<%= paths.wffmDist %>/<%= paths.wffmCssFileName %>.css': '<%= paths.wffmSrc %>/_web-forms.less'
}
}
};Less build task
Solution explorer
.define(@var) {
@header: 'h@{var}';
@header-mobile: 'mobile-h@{var}';
}
.h(@index) when (@index > 0) {
h@{index} {
.define(@index);
font-size: @base-font-size * @@header-mobile;
@media @tablet {
font-size: @base-font-size * @@header;
}
}
.h(@index - 1);
}
.h(0) {
}
.h(5);
@import (reference) "_breakpoints.less";
@import (reference) "_variables.less";
.btn {
height: 50px;
line-height: 52px;
min-width: 165px;
padding: 0 50px 0 20px;
border: none;
border-radius: 30px;
box-sizing: border-box;
display: inline-block;
text-align: left;
cursor: pointer;
margin-top: @division-small;
transition: opacity 0.25s @cubic-1;
&:hover,
&:focus {
opacity: 0.5;
}
&--base {
background: fade(@dark-grey, 30%) url(/dist/images/icons/Icons_Arrow.svg) no-repeat center right 20px;
background-size: 18px 18px;
}
&--cta {
height: 60px;
line-height: 62px;
background: @bs-magenta url(/dist/images/icons/Icons_Arrow_White.svg) no-repeat center right 20px;
color: @white;
background-size: 18px 18px;
}
&--ghost {
background: fade(@white, 30%) url(/dist/images/icons/Icons_Arrow_White.svg) no-repeat center right 20px;
color: @white;
background-size: 18px 18px;
}
}Could this be stored in GitHub? Or separated out into its own project and Nuget package?