Commit b4a1a902 authored by Benjamin Kott's avatar Benjamin Kott Committed by Christian Kuhn
Browse files

[TASK] Migrate backend styles from LESS to SASS

In preparation for upcomming switch from bootstrap to SASS the backend
style sources have been migrated to SASS. The autoprefixer configuration
was corrected to match CMS8 LTS requirements and minification of CSS
files is now enabled. Automatic style checks are now available through
grunt. The tasks `grunt format` and `grunt lint` will now check sources
files for consistency.

Resolves: #80453
Releases: master
Change-Id: I9ba23aea78014a1f0cce229db533a81d5ffcd02f
Reviewed-on: https://review.typo3.org/52151

Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Marco Christian Krenn's avatarMarco Christian Krenn <krenn@webconsulting.at>
Tested-by: Marco Christian Krenn's avatarMarco Christian Krenn <krenn@webconsulting.at>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent b4f3aee1
......@@ -2,15 +2,13 @@
# top-most EditorConfig file
root = true
charset = utf-8
# Get rid of whitespace to avoid diffs with a bunch of EOL changes
trim_trailing_whitespace = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# CSS-Files
[*.css]
......@@ -32,6 +30,11 @@ indent_size = 4
indent_style = tab
indent_size = 4
# SCSS-Files
[*.scss]
indent_style = space
indent_size = 4
# JS-Files
[*.js]
indent_style = tab
......@@ -62,8 +65,10 @@ indent_size = 4
indent_style = space
indent_size = 2
# package.json or .travis.yml
[{package.json,.travis.yml}]
# package.json
# .travis.yml
# bower.json
[{package.json,.travis.yml,bower.json}]
indent_style = space
indent_size = 2
......
......@@ -12,6 +12,7 @@
*.css text eol=lf
*.tmpl text eol=lf
*.less text eol=lf
*.scss text eol=lf
*.js text eol=lf
*.json text eol=lf
*.php text eol=lf
......
{
"rules": {
"at-rule-empty-line-before": [
"always",
{
"except": [
"blockless-after-same-name-blockless",
"first-nested"
],
"ignore": [
"after-comment"
]
}
],
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-semicolon-newline-after": "always",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always-multi-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always-multi-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short",
"color-no-invalid-hex": true,
"comment-empty-line-before": [
"always",
{
"except": [
"first-nested"
],
"ignore": [
"stylelint-commands"
]
}
],
"comment-no-empty": true,
"comment-whitespace-inside": "always",
"custom-property-empty-line-before": [
"always",
{
"except": [
"after-custom-property",
"first-nested"
],
"ignore": [
"after-comment",
"inside-single-line-block"
]
}
],
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [
true,
{
"ignore": [
"consecutive-duplicates-with-different-values"
]
}
],
"declaration-block-no-redundant-longhand-properties": true,
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"declaration-empty-line-before": [
"always",
{
"except": [
"after-declaration",
"first-nested"
],
"ignore": [
"after-comment",
"inside-single-line-block"
]
}
],
"font-family-no-duplicate-names": true,
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-whitespace-after": "always",
"indentation": 4,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": true,
"max-empty-lines": 1,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-unknown": true,
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"property-case": "lower",
"property-no-unknown": true,
"rule-empty-line-before": [
"always-multi-line",
{
"except": [
"first-nested"
],
"ignore": [
"after-comment"
]
}
],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "single",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"shorthand-property-no-redundant-values": true,
"string-no-newline": true,
"unit-case": "lower",
"unit-no-unknown": true,
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0
}
}
......@@ -13,24 +13,41 @@
module.exports = function(grunt) {
/**
* Grunt stylefmt task
*/
grunt.registerMultiTask('formatsass', 'Grunt task for stylefmt', function () {
var options = this.options(),
done = this.async(),
stylefmt = require('stylefmt'),
scss = require('postcss-scss'),
files = this.filesSrc.filter(function (file) {
return grunt.file.isFile(file);
}),
counter = 0;
this.files.forEach(function (file) {
file.src.filter(function (filepath) {
var content = grunt.file.read(filepath);
var settings = {
from: filepath,
syntax: scss
};
stylefmt.process(content, settings).then(function (result) {
grunt.file.write(file.dest, result.css);
grunt.log.success('Source file "' + filepath + '" was processed.');
counter++;
if (counter >= files.length) done(true);
});
});
});
});
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
banner: '/*!\n' +
' * This file is part of the TYPO3 CMS project.\n' +
' *\n' +
' * It is free software; you can redistribute it and/or modify it under\n' +
' * the terms of the GNU General Public License, either version 2\n' +
' * of the License, or any later version.\n' +
' *\n' +
' * For the full copyright and license information, please read the\n' +
' * LICENSE.txt file that was distributed with this source code.\n' +
' *\n' +
' * The TYPO3 project - inspiring people to share!\n' +
' */\n',
paths: {
resources : 'Resources/',
less : '<%= paths.resources %>Public/Less/',
sass : '<%= paths.resources %>Public/Sass/',
root : '../',
sysext : '<%= paths.root %>typo3/sysext/',
form : '<%= paths.sysext %>form/Resources/',
......@@ -47,51 +64,73 @@ module.exports = function(grunt) {
t3icons : '<%= paths.bower %>typo3-icons/dist/',
npm : 'node_modules/'
},
less: {
stylelint: {
options: {
banner: '<%= banner %>',
outputSourceFiles: true
configFile: '<%= paths.root %>.stylelintrc',
},
sass: ['<%= paths.sass %>**/*.scss']
},
formatsass: {
sass: {
files: [{
expand: true,
cwd: '<%= paths.sass %>',
src: ['**/*.scss'],
dest: '<%= paths.sass %>'
}]
}
},
sass: {
options: {
outputStyle: 'expanded',
precision: 8,
includePaths: [
'bower_components/bootstrap-sass/assets/stylesheets',
'bower_components/fontawesome/scss',
'bower_components/eonasdan-bootstrap-datetimepicker/src/sass',
'node_modules/tagsort'
]
},
backend: {
files: {
"<%= paths.backend %>Public/Css/backend.css": "<%= paths.less %>backend.less"
"<%= paths.backend %>Public/Css/backend.css": "<%= paths.sass %>backend.scss"
}
},
core: {
files: {
"<%= paths.core %>Public/Css/errorpage.css": "<%= paths.less %>errorpage.less"
"<%= paths.core %>Public/Css/errorpage.css": "<%= paths.sass %>errorpage.scss"
}
},
form: {
files: {
"<%= paths.form %>Public/Css/form.css": "<%= paths.less %>form.less"
"<%= paths.form %>Public/Css/form.css": "<%= paths.sass %>form.scss"
}
},
frontend: {
files: {
"<%= paths.frontend %>Public/Css/adminpanel.css": "<%= paths.less %>adminpanel.less"
"<%= paths.frontend %>Public/Css/adminpanel.css": "<%= paths.sass %>adminpanel.scss"
}
},
install: {
files: {
"<%= paths.install %>Public/Css/install.css": "<%= paths.less %>install.less"
"<%= paths.install %>Public/Css/install.css": "<%= paths.sass %>install.scss"
}
},
linkvalidator: {
files: {
"<%= paths.linkvalidator %>Public/Css/linkvalidator.css": "<%= paths.less %>linkvalidator.less"
"<%= paths.linkvalidator %>Public/Css/linkvalidator.css": "<%= paths.sass %>linkvalidator.scss"
}
},
workspaces: {
files: {
"<%= paths.workspaces %>Public/Css/preview.css": "<%= paths.workspaces %>Private/Less/preview.less"
"<%= paths.workspaces %>Public/Css/preview.css": "<%= paths.sass %>workspace.scss"
}
},
t3editor: {
files: {
'<%= paths.t3editor %>Public/Css/t3editor.css': '<%= paths.t3editor %>Private/Less/t3editor.less',
'<%= paths.t3editor %>Public/Css/t3editor_inner.css': '<%= paths.t3editor %>Private/Less/t3editor_inner.less',
'<%= paths.t3editor %>Public/Css/t3editor_typoscript_colors.css': '<%= paths.t3editor %>Private/Less/t3editor_typoscript_colors.less'
'<%= paths.t3editor %>Public/Css/t3editor.css': '<%= paths.sass %>editor.scss',
'<%= paths.t3editor %>Public/Css/t3editor_inner.css': '<%= paths.sass %>editor_inner.scss',
'<%= paths.t3editor %>Public/Css/t3editor_typoscript_colors.css': '<%= paths.sass %>editor_typoscript_colors.scss'
}
}
},
......@@ -99,12 +138,34 @@ module.exports = function(grunt) {
options: {
map: false,
processors: [
require('autoprefixer')({ // add vendor prefixes
require('autoprefixer')({
browsers: [
'Last 2 versions',
'Firefox ESR',
'IE 11'
'Chrome >= 57',
'Firefox >= 52',
'Edge >= 14',
'Explorer >= 11',
'iOS >= 9',
'Safari >= 8',
'Android >= 4',
'Opera >= 43'
]
}),
require('postcss-clean')({
keepSpecialComments: 0
}),
require('postcss-banner')({
banner: 'This file is part of the TYPO3 CMS project.\n' +
'\n' +
'It is free software; you can redistribute it and/or modify it under\n' +
'the terms of the GNU General Public License, either version 2\n' +
'of the License, or any later version.\n' +
'\n' +
'For the full copyright and license information, please read the\n' +
'LICENSE.txt file that was distributed with this source code.\n' +
'\n' +
'The TYPO3 project - inspiring people to share!',
important: true,
inline: false
})
]
},
......@@ -156,8 +217,8 @@ module.exports = function(grunt) {
options: {
livereload: true
},
less: {
files: '<%= paths.less %>**/*.less',
sass: {
files: '<%= paths.sass %>**/*.scss',
tasks: 'css'
},
ts: {
......@@ -349,7 +410,7 @@ module.exports = function(grunt) {
});
// Register tasks
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-bowercopy');
grunt.loadNpmTasks('grunt-npm-install');
......@@ -360,6 +421,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks("grunt-ts");
grunt.loadNpmTasks('grunt-tslint');
grunt.loadNpmTasks('grunt-stylelint');
/**
* grunt default task
......@@ -370,16 +432,27 @@ module.exports = function(grunt) {
*/
grunt.registerTask('default', ['css']);
/**
* grunt format
*
* call "$ grunt format"
*
* this task does the following things:
* - formatsass
* - lint
*/
grunt.registerTask('format', ['formatsass', 'stylelint']);
/**
* grunt css task
*
* call "$ grunt css"
*
* this task does the following things:
* - less
* - sass
* - postcss
*/
grunt.registerTask('css', ['less', 'postcss']);
grunt.registerTask('css', ['sass', 'postcss']);
/**
* grunt update task
......@@ -417,10 +490,10 @@ module.exports = function(grunt) {
* this task does the following things:
* - execute update task
* - execute copy task
* - compile less files
* - compile sass files
* - uglify js files
* - minifies svg files
* - compiles TypeScript files
*/
grunt.registerTask('build', ['update', 'scripts', 'copy', 'css', 'uglify', 'svgmin']);
grunt.registerTask('build', ['update', 'scripts', 'copy', 'format', 'css', 'uglify', 'svgmin']);
};
//
// Autocomplete
// ============
//
//
// Variables
//
@autocomplete-border: #ddd;
@autocomplete-border-radius: 2px;
@autocomplete-results-bg: #fff;
@autocomplete-zindex: @zindex-dropdown;
@autocomplete-suggestion-link-hover-bg: #fafafa;
//
// Component
//
.autocomplete {
position: relative;
}
.autocomplete-results {
z-index: @autocomplete-zindex;
position: absolute;
margin: 5px 0;
top: 100%;
left: 0;
border: 1px solid @autocomplete-border;
border-radius: @autocomplete-border-radius;
background-color: @autocomplete-results-bg;
overflow: hidden;
box-shadow: 0 1px 0 0 rgba(0,0,0,0.25);
}
.autocomplete-suggestion {
border-top: 1px solid @autocomplete-border;
&:first-child {
border-top: none;
}
}
.autocomplete-suggestion-link {
padding: 5px 13px 5px 28px;
display: block;
text-decoration: none;
.autocomplete-selected &,
&:hover {
background-color: @autocomplete-suggestion-link-hover-bg;
text-decoration: none;
}
}
.autocomplete-info {
padding: 5px 15px;
}
//
// Dropdowns
//
.dropdown-menu {
line-height: 1.45em;
border: 0;
margin: 0;
border-radius: 0;
color: @dropdown-color;
a {
color: inherit;
display: block;
&:focus,
&:hover {
color: inherit;
text-decoration: none;
}
}
hr {
border-top: 1px solid rgba(0,0,0,0.25);
margin: 1.25em -15px;
}
> *:last-child {
margin-bottom: 0;
}
.form-group {
margin-bottom: 0.75em;
}
.form-control {
border-color: #aaaaaa;
color: inherit;
border-radius: 0;
background-color: #333333;
&:focus {
box-shadow: none;
border-color: #bbbbbb;
}
}
.btn {
border: none;
border-radius: 0;
padding: 6px 10px;
}
}
.dropdown-headline {
font-size: 1.15em;
margin-top: 0;
margin-bottom: 0.5em;
}
.dropdown-text {
a {
display: inline-block;
}
}
div.dropdown-menu {
padding: 1.5em;
}
.dropdown-list {
.list-unstyled();
> li {
&:extend(.clearfix all);
position: relative;
}
> li + li {
margin-top: 0.5em;
}
}
.dropdown-list-link {
display: block;
text-decoration: none;
&:hover,
&:focus {
text-decoration: none;
}
}
//
// Dropdown Table
//
.dropdown-table {
display: table;
width: 100%;
}
.dropdown-table-row {
display: table-row;
}
.dropdown-table-column {
display: table-cell;
padding-top: 0.25em;
padding-bottom: 0.25em;
vertical-align: middle;
}
.dropdown-table-column-top {
vertical-align: top;
}
.dropdown-table-icon {
width: 16px;
padding-right: 0.5em;
}
.dropdown-table-title {
white-space: nowrap;
padding-right: 1.5em;
}
.dropdown-table-title-ellipsis {
max-width: 230px;
overflow: hidden;
display: block;
white-space: nowrap;
text-overflow: ellipsis;
}
.dropdown-table-actions {
white-space: nowrap;
text-align: right;
}
.dropdown-table-actions-btn {
text-align: center;