Commit 4fcc6cde authored by Nikolay Gromov's avatar Nikolay Gromov

Налажена компиляция Sass

parent a5df6757
'use strict'; 'use strict';
// Получение настроек папок из package.json // Подключим зависимости
const pjson = require('./package.json'); const fs = require('fs');
const dirs = pjson.config.directories;
const ghPagesUrl = pjson.config.ghPages;
// Зависимости проекта
const gulp = require('gulp'); const gulp = require('gulp');
const less = require('gulp-less'); const gulpSequence = require('gulp-sequence');
const sass = require('gulp-sass');
const postcss = require('gulp-postcss');
const autoprefixer = require("autoprefixer")
const mqpacker = require("css-mqpacker")
const notify = require('gulp-notify');
const gulpIf = require('gulp-if');
const debug = require('gulp-debug'); const debug = require('gulp-debug');
const sourcemaps = require('gulp-sourcemaps'); const sourcemaps = require('gulp-sourcemaps');
const cleanss = require('gulp-cleancss'); const cleanss = require('gulp-cleancss');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const mqpacker = require('css-mqpacker');
const rename = require('gulp-rename'); const rename = require('gulp-rename');
const gulpIf = require('gulp-if');
const del = require('del');
const imagemin = require('gulp-imagemin');
const pngquant = require('imagemin-pngquant');
const svgstore = require('gulp-svgstore');
const svgmin = require('gulp-svgmin');
const path = require('path');
const cheerio = require('gulp-cheerio');
const fileinclude = require('gulp-file-include');
const newer = require('gulp-newer');
const notify = require('gulp-notify');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const browserSync = require('browser-sync').create();
const replace = require('gulp-replace');
const ghPages = require('gulp-gh-pages');
const size = require('gulp-size'); const size = require('gulp-size');
const fs = require('fs'); const del = require('del');
// Получим настройки проекта из package.json
let pjson = require('./package.json');
let dirs = pjson.configProject.dirs;
let lists = getFilesList(pjson.configProject);
console.log('---------- Файлы и папки, взятые в работу:');
console.log(lists);
// Запишем стилевой файл диспетчер подключений
let styleImports = '/**\n * ВНИМАНИЕ! Этот файл генерируется автоматически.\n * Не пишите сюда ничего вручную, все такие правки будут потеряны.\n * Читайте ./README.md для понимания.\n */\n\n';
lists.css.forEach(function(blockPath) {
styleImports += '@import "'+blockPath+'";\n';
});
fs.writeFileSync('./src/scss/style.scss', styleImports);
// Запуск `NODE_ENV=production npm start [задача]` приведет к сборке без sourcemaps // Запуск `NODE_ENV=production npm start [задача]` приведет к сборке без sourcemaps
const isDev = !process.env.NODE_ENV || process.env.NODE_ENV == 'dev'; const isDev = !process.env.NODE_ENV || process.env.NODE_ENV == 'dev';
// Запуск `port=3004 npm start` приведет к запуску сервера обновлений на 3004 порту и всей обычной автоматизации // Очистка папки сборки
const port = process.env.port ? process.env.port : 3000; gulp.task('clean', function () {
console.log('---------- Очистка папки сборки');
// Файлы компилируемых компонентов return del([
let blocks = getComponentsFiles(); dirs.buildPath + '/**/*',
'!' + dirs.buildPath + '/readme.md'
// Вывод в консоль информации о взятых в сборку файлах (без LESS) ]);
if(blocks.js.length) { });
console.log('---------- В сборку и обработку взяты JS-файлы (указана последовательность):');
console.log(blocks.js);
}
if(blocks.img.length) {
console.log('---------- В сборку и обработку взяты изображения:');
console.log(blocks.img);
}
if(blocks.additionalCss.length) {
console.log('---------- В сборку скопированы добавочные CSS:');
console.log(blocks.additionalCss);
}
// Компиляция LESS // Компиляция стилей
gulp.task('less', function () { gulp.task('style', function () {
console.log('---------- Компиляция LESS'); console.log('---------- Компиляция стилей');
return gulp.src(dirs.source + '/less/style.less') return gulp.src(dirs.srcPath + 'scss/style.scss')
.pipe(gulpIf(isDev, sourcemaps.init())) .pipe(gulpIf(isDev, sourcemaps.init()))
.pipe(debug({title: "LESS:"})) .pipe(debug({title: "Style:"}))
.pipe(less()) .pipe(sass())
.on('error', notify.onError(function(err){
return {
title: 'Styles compilation error',
message: err.message
}
}))
.pipe(postcss([ .pipe(postcss([
autoprefixer({browsers: ['last 2 version']}), autoprefixer({browsers: ['last 2 version']}),
mqpacker({ mqpacker({
sort: true sort: true
}), }),
])) ]))
.pipe(gulpIf(!isDev, cleanss()))
.pipe(rename('style.min.css'))
.pipe(debug({title: "RENAME:"}))
.pipe(gulpIf(isDev, sourcemaps.write('/')))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.build + '/css'))
.pipe(browserSync.stream());
});
// Копирование добавочных CSS, которые хочется иметь отдельными файлами
gulp.task('copy:css', function(callback) {
if(blocks.additionalCss.length > 0) {
console.log('---------- копирование CSS');
return gulp.src(blocks.additionalCss, {since: gulp.lastRun('copy:css')})
.pipe(postcss([
autoprefixer({browsers: ['last 2 version']}),
mqpacker
]))
.pipe(cleanss())
.pipe(gulp.dest(dirs.build + '/css'));
}
else {
console.log('---------- Копирование CSS: нет дополнительного CSS');
callback();
}
});
// Копирование и оптимизация изображений
gulp.task('img', function () {
console.log('---------- Копирование и оптимизация картинок');
return gulp.src(blocks.img, {since: gulp.lastRun('img')}) // только для изменившихся с последнего запуска файлов
.pipe(newer(dirs.build + '/img')) // оставить в потоке только изменившиеся файлы
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant()]
}))
.pipe(gulp.dest(dirs.build + '/img'));
});
// Оптимизация изображений // folder=src/img/icons/ npm start img:opt
const folder = process.env.folder;
gulp.task('img:opt', function (callback) {
if(folder){
console.log('---------- Оптимизация картинок');
return gulp.src(folder + '/*.{jpg,jpeg,gif,png,svg}')
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant()]
}))
.pipe(gulp.dest(folder));
}
else {
console.log('---------- Оптимизация картинок: ошибка (не указана папка)');
console.log('---------- Пример вызова команды: folder=src/blocks/block-name/img_to_bg/ npm start img:opt');
callback();
}
});
// Копирование шрифтов
gulp.task('fonts:copy', function () {
console.log('---------- Копирование шрифтов');
return gulp.src(dirs.source + '/fonts/*.{ttf,woff,woff2,eot,svg}', {since: gulp.lastRun('fonts:copy')})
.pipe(newer(dirs.build + '/fonts')) // оставить в потоке только изменившиеся файлы
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.build + '/fonts'));
});
// Сборка SVG-спрайта для блока sprite-svg--localstorage
gulp.task('svgstore', function (callback) {
let spritePath = dirs.source + '/blocks/sprite-svg--localstorage/svg/';
if(fileExist(spritePath) !== false) {
console.log('---------- Сборка SVG спрайта');
return gulp.src(spritePath + '*.svg')
.pipe(svgmin(function (file) {
return {
plugins: [{
cleanupIDs: {
minify: true
}
}]
}
}))
.pipe(svgstore({ inlineSvg: true }))
.pipe(cheerio(function ($) {
$('svg').attr('style', 'display:none');
}))
.pipe(rename('sprite-svg--ls.svg'))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.source + '/blocks/sprite-svg--localstorage/img'));
}
else {
console.log('---------- Сборка SVG спрайта: нет папки с картинками');
callback();
}
});
// Сборка HTML
gulp.task('html', function() {
console.log('---------- сборка HTML');
return gulp.src(dirs.source + '/*.html')
.pipe(fileinclude({
prefix: '@@',
basepath: '@file',
indent: true,
}))
.pipe(replace(/\n\s*<!--DEV[\s\S]+?-->/gm, ''))
.pipe(gulp.dest(dirs.build));
});
// Конкатенация и углификация Javascript
gulp.task('js', function (callback) {
if(blocks.js.length > 0){
console.log('---------- Обработка JS');
return gulp.src(blocks.js)
.pipe(gulpIf(isDev, sourcemaps.init()))
.pipe(concat('script.min.js'))
.pipe(gulpIf(!isDev, uglify()))
.on('error', notify.onError(function(err){ .on('error', notify.onError(function(err){
return { return {
title: 'Javascript uglify error', title: 'Styles compilation error',
message: err.message message: err.message
} }
})) }))
.pipe(gulpIf(isDev, sourcemaps.write('.'))) .pipe(gulpIf(!isDev, cleanss()))
.pipe(rename('style.min.css'))
.pipe(gulpIf(isDev, sourcemaps.write('/')))
.pipe(size({ .pipe(size({
title: 'Размер', title: 'Размер',
showFiles: true, showFiles: true,
showTotal: false, showTotal: false,
})) }))
.pipe(gulp.dest(dirs.build + '/js')); .pipe(gulp.dest(dirs.buildPath + '/css'));
} // .pipe(browserSync.stream());
else {
console.log('---------- Обработка JS: в сборке нет JS-файлов');
callback();
}
}); });
// Копирование JS-библиотек (если есть)
gulp.task('js:copy', function (callback) {
let jsLibs = [];
if(fileExistAndHasContent(dirs.source + '/js/jquery.3.1.1.min.js')) {
jsLibs.push(dirs.source + '/js/jquery.3.1.1.min.js');
}
if(jsLibs){
console.log('---------- Копирование JS-библиотек');
return gulp.src(jsLibs)
.pipe(gulp.dest(dirs.build + '/js'));
}
else {
callback();
}
});
// Очистка папки сборки
gulp.task('clean', function () {
console.log('---------- Очистка папки сборки');
return del([
dirs.build + '/**/*',
'!' + dirs.build + '/readme.md'
]);
});
// Сборка всего
gulp.task('build', gulp.series(
'clean',
'svgstore',
gulp.parallel('less', 'copy:css', 'img', 'js', 'js:copy', 'fonts:copy'),
'html'
));
// Локальный сервер, слежение
gulp.task('serve', gulp.series('build', function() {
browserSync.init({
server: dirs.build,
port: port,
startPath: 'index.html'
});
gulp.watch([
dirs.source + '/*.html',
dirs.source + '/_include/*.html',
dirs.source + '/blocks/**/*.html',
], gulp.series('html', reloader));
gulp.watch(blocks.less, gulp.series('less'));
if(blocks.img) {
gulp.watch(blocks.img, gulp.series('img', reloader));
}
if(blocks.js) {
gulp.watch(blocks.js, gulp.series('js', reloader));
}
gulp.watch(dirs.source + '/fonts/*.{ttf,woff,woff2,eot,svg}', gulp.series('fonts:copy', reloader));
}));
// Отправка в GH pages (ветку gh-pages репозитория)
gulp.task('deploy', function() {
console.log('---------- Публикация ./build/ на GH pages');
console.log('---------- '+ ghPagesUrl);
return gulp.src('./build/**/*')
.pipe(ghPages());
});
// Задача по умолчанию /**
gulp.task('default', * Вернет объект с обрабатываемыми файлами и папками
gulp.series('serve') * @param {object}
); * @return {object}
*/
function getFilesList(config){
// Перезагрузка в браузере let res = {
function reloader(done) { 'css': [],
browserSync.reload(); 'js': [],
done(); 'img': [],
}
// Определение собираемых компонентов
function getComponentsFiles() {
// Создаем объект для служебных данных
let сomponentsFilesList = {
less: [], // тут будут LESS-файлы в том же порядке, в котором они подключены
js: [], // тут будут JS-файлы в том же порядке, в котором подключены LESS-файлы
img: [], // тут будет массив из «путь_до_блока/img/*.{jpg,jpeg,gif,png,svg}» для всех импортируемых блоков
additionalCss: [], // тут будут добавочные CSS-файлы блоков в том же порядке, в котором подключены LESS-файлы
}; };
// Читаем файл диспетчера подключений // Style
let connectManager = fs.readFileSync(dirs.source + '/less/style.less', 'utf8'); for (let blockName in config.blocks) {
res.css.push(config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/' + blockName + '.scss');
// Делаем из строк массив, фильтруем массив, оставляя только строки с незакомментированными импортами if(config.blocks[blockName].length) {
let fileSystem = connectManager.split('\n').filter(function(item) { config.blocks[blockName].forEach(function(elementName) {
if(/^(\s*)@import/.test(item)) return true; res.css.push(config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/' + blockName + elementName + '.scss');
else return false;
}); });
// Обойдём массив и запишем его части в объект результирующей переменной
fileSystem.forEach(function(item, i) {
// Попробуем вычленить блок из строки импорта
let componentData = /\/blocks\/(.+?)(\/)(.+?)(?=.(less|css))/g.exec(item);
// Если это блок и получилось извлечь имя файла
if (componentData !== null && componentData[3]) {
let componentName = componentData[1]; // Название блока
let blockDir = dirs.source + '/blocks/' + componentName; // Папка блока
let componentFileName = componentData[3]; // Имя подключаемого файла без расширения
let jsFile = blockDir + '/' + componentFileName + '.js'; // Имя JS-файла, который нужно взять в сборку
let cssFile = blockDir + '/' + componentFileName + '.css'; // Имя CSS-файла, который нужно обработать
let imagesDir = blockDir + '/img'; // Папка с картинками, которую нужно взять в обработку
// Добавляем в массив с результатом LESS-файл
сomponentsFilesList.less.push(dirs.source + componentData[0] + '.' + componentData[4]);
// Если существует JS-файл — добавляем его в массив с результатом
if(fileExistAndHasContent(jsFile)) {
сomponentsFilesList.js.push(jsFile);
} }
// Если существует CSS-файл — добавляем его в массив с результатом
if(fileExistAndHasContent(cssFile)) {
сomponentsFilesList.additionalCss.push(cssFile);
} }
res.css = res.css.concat(config.addCssAfter);
res.css = config.addCssBefore.concat(res.css);
// Если есть папка с изображениями, добавляем её в массив с результатом // JS
if(fileExist(imagesDir) !== false) { for (let blockName in config.blocks) {
сomponentsFilesList.img.push(imagesDir + '/*.{jpg,jpeg,gif,png,svg}'); res.js.push(config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/' + blockName + '.js');
} if(config.blocks[blockName].length) {
} config.blocks[blockName].forEach(function(elementName) {
res.js.push(config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/' + blockName + elementName + '.js');
}); });
// Добавим глобальныe LESS-файлы в массив с обрабатываемыми LESS-файлами
сomponentsFilesList.less.push(dirs.source + '/less/**/*.less');
// Добавим глобальный JS-файл в начало массива с обрабатываемыми JS-файлами
if(fileExistAndHasContent(dirs.source + '/js/global-script.js')) {
сomponentsFilesList.js.unshift(dirs.source + '/js/global-script.js');
}
// Если хочется иметь jQuery в конкатенируемом JS, раскомментируйте эти строки
// if(fileExistAndHasContent(dirs.source + '/js/jquery.js')) {
// сomponentsFilesList.js.unshift(dirs.source + '/js/jquery.js'); // добавляем в начало
// }
// Если хочется иметь в конкатенируемом JS ещё какие-то файлы, пишите это здесь
// if(fileExistAndHasContent(dirs.source + '/js/file_name.js')) {
// сomponentsFilesList.js.unshift(dirs.source + '/js/file_name.js'); // добавляем в начало
// или
// сomponentsFilesList.js.push(dirs.source + '/js/file_name.js'); // добавляем в конец
// }
// Добавим глобальный CSS-файл в начало массива с обрабатываемыми CSS-файлами
if(fileExistAndHasContent(dirs.source + '/css/global-css.css')) {
сomponentsFilesList.additionalCss.unshift(dirs.source + '/css/global-css.css');
} }
// Если хочется иметь в папке сборки какие-то еще отдельные CSS-файлы, пишите их здесь
// if(fileExistAndHasContent(dirs.source + '/css/file_name.css')) {
// сomponentsFilesList.additionalCss.unshift(dirs.source + '/css/file_name.css');
// }
// Добавим глобальные изображения
сomponentsFilesList.img.unshift(dirs.source + '/img/*.{jpg,jpeg,gif,png,svg}');
сomponentsFilesList.img = uniqueArray(сomponentsFilesList.img);
// Возвращаем объект со служебными данными
return сomponentsFilesList;
}
// Проверка существования файла и его размера (размер менее 2байт == файла нет)
function fileExistAndHasContent(path) {
const fs = require('fs');
try {
fs.statSync(path);
if(fs.statSync(path).size > 1) return true;
else return false;
} catch(err) {
return !(err && err.code === 'ENOENT');
} }
} res.js = res.js.concat(config.addJsAfter);
res.js = config.addJsBefore.concat(res.js);
// Проверка существования файла // Images
function fileExist(path) { for (let blockName in config.blocks) {
const fs = require('fs'); res.img.push(config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/img');
try {
fs.statSync(path);
} catch(err) {
return !(err && err.code === 'ENOENT');
} }
} res.img = config.addImages.concat(res.img);
return res;
// Оставить в массиве только уникальные значения (убрать повторы)
function uniqueArray(arr) {
var objectTemp = {};
for (var i = 0; i < arr.length; i++) {
var str = arr[i];
objectTemp[str] = true; // запомнить строку в виде свойства объекта
}
return Object.keys(objectTemp);
} }
{ {
"name": "NTH-start-project", "name": "NTH-start-project",
"version": "1.1.0", "version": "2.0.0",
"description": "Start a library of blocks and technical solutions for HTML/CSS/js page layout.", "description": "Start a library of blocks and technical solutions for HTML/CSS/js page layout.",
"scripts": {
"test": "stylelint \"src/**/*.less\"",
"start": "gulp",
"deploy": "cross-env NODE_ENV=production ./node_modules/.bin/gulp build && cross-env ./node_modules/.bin/gulp deploy",
"build": "cross-env NODE_ENV=production npm start build",
"precommit": "npm test"
},
"author": "Nikolay Gromov", "author": "Nikolay Gromov",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/nicothin/NTH-start-project.git" "url": "https://github.com/nicothin/NTH-start-project.git"
}, },
"configProject": {
"blocks": {
"page-header": [],
"page-footer": []
},
"addCssBefore": [
"./src/scss/variables.scss"
],
"addCssAfter": [
"./src/scss/print.scss"
],
"addJsBefore": [
"./src/js/jquery.3.1.1.min.js"
],
"addJsAfter": [
"./src/js/global-script.js"
],
"addImages": [
"./src/img/"
],
"dirs": {
"srcPath": "./src/",
"buildPath": "./build/",
"blocksDirName": "blocks"
}
},
"scripts": {
"test": "stylelint \"src/**/*.less\"",
"start": "gulp",
"deploy": "cross-env NODE_ENV=production ./node_modules/.bin/gulp build && cross-env ./node_modules/.bin/gulp deploy",
"build": "cross-env NODE_ENV=production npm start build"
},
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.3.3", "autoprefixer": "^6.7.7",
"browser-sync": "^2.11.1",
"cross-env": "^3.1.4",
"css-mqpacker": "^5.0.1", "css-mqpacker": "^5.0.1",
"del": "^2.2.0", "del": "^2.2.2",
"gulp": "github:gulpjs/gulp#4.0", "gulp": "^3.9.1",
"gulp-cheerio": "^0.6.2",
"gulp-cleancss": "^0.2.2", "gulp-cleancss": "^0.2.2",
"gulp-concat": "^2.6.0", "gulp-debug": "^3.1.0",
"gulp-debug": "^3.0.0", "gulp-if": "^2.0.2",
"gulp-file-include": "^1.0.0",
"gulp-gh-pages": "^0.5.4",
"gulp-if": "^2.0.0",
"gulp-imagemin": "^3.1.1",
"gulp-less": "^3.1.0",
"gulp-newer": "^1.1.0",
"gulp-notify": "^3.0.0", "gulp-notify": "^3.0.0",
"gulp-postcss": "^6.1.0", "gulp-postcss": "^6.3.0",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-replace": "^0.5.4", "gulp-sass": "^3.1.0",
"gulp-sequence": "^0.4.6",
"gulp-size": "^2.1.0", "gulp-size": "^2.1.0",
"gulp-sourcemaps": "^2.4.1", "gulp-sourcemaps": "^2.4.1"
"gulp-svgmin": "^1.2.1",
"gulp-svgstore": "^6.1.0",
"gulp-uglify": "^2.0.1",
"husky": "^0.13.1",
"imagemin-pngquant": "^5.0.0",
"mkdirp": "^0.5.1",
"stylelint": "^7.1.0",
"stylelint-declaration-use-variable": "^1.5.0"
},
"config": {
"ghPages": "http://nicothin.github.io/NTH-start-project/index.html",
"directories": {
"source": "./src",
"build": "./build"
}
} }
} }
// Для импорта в диспетчер подключений: @import './src/blocks/page-footer/page-footer.less';
@import '../../less/variables.less'; // только для удобства обращения к переменным
.page-footer {
}
// Для импорта в диспетчер подключений: @import './src/blocks/page-header/page-header.less';
@import '../../less/variables.less'; // только для удобства обращения к переменным
.page-header {
}
@media print {
*,
*:before,
*:after {
background: transparent !important;
color: #000 !important;
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline !important;
}
a[href]:after {
content: ' (' attr(href) ')';
}
abbr[title]:after {
content: ' (' attr(title) ')';
}
a[href^='#']:after,
a[href^='javascript:']:after {
content: '';
}
pre,
blockquote {
border: 1px solid #999 !important;
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
table {
border-collapse: collapse !important;
td,
th {
background-color: #fff !important;
}
}
}
/**
* ВНИМАНИЕ! Этот файл генерируется автоматически.
* Не пишите сюда ничего вручную, все такие правки будут потеряны.
* Читайте ./README.md для понимания.
*/
@import "./src/scss/variables.scss";
@import "./src/blocks/page-header/page-header.scss";
@import "./src/blocks/page-footer/page-footer.scss";
@import "./src/scss/print.scss";
// Базовые цвета https://colorscheme.ru/color-converter.html
$black: hsl(0, 0%, 0%);
$gray-darkest: hsl(0, 0%, 10%);
$gray-darker: hsl(0, 0%, 20%);
$gray-darken: hsl(0, 0%, 30%);
$gray-dark: hsl(0, 0%, 40%);
$gray: hsl(0, 0%, 50%);
$gray-light: hsl(0, 0%, 60%);
$gray-lighten: hsl(0, 0%, 70%);
$gray-lighter: hsl(0, 0%, 80%);
$gray-lightest: hsl(0, 0%, 90%);
$white: hsl(0, 0%, 100%);
$color-main: hsl(208, 98%, 43%);
$color-success: hsl(120, 39%, 54%);
$color-danger: hsl(2, 64%, 58%);
// Семантические цвета
$text-color: $gray-darkest;
$text-color--muted: $gray;
$body-bg: #fff;
$link-color: $color-main;
$link-color--hover: darken($color-main, 15%);
$border-color: $gray-light;
// Базовая типографика
$font-size: 14px;
$font-size--h1: 2.25em;
$font-size--h2: 1.875em;
$font-size--h3: 1.5em;
$font-size--h4: 1.25em;
$font-size--h5: 1em;
$font-size--h6: 1em;
$font-size--small: 0.75em;
$line-height: 1.375;
$font-family: -apple-system, BlinkMacSystemFont, 'Roboto', 'Ubuntu', 'Droid Sans', 'Helvetica Neue', 'Arial', sans-serif;
$font-family--serif: 'Georgia', 'Times New Roman', 'Times', serif;
$font-family--monospace: 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
$font-family--headings: $font-family;
// Ширины
$screen-xs: 0;
$screen-sm: 480px;
$screen-md: 768px;
$screen-lg: 992px;
$screen-xl: 1200px;
$screen-xxl: 1800px;
$container-sm: 100%;
$container-md: 100%;
$container-lg: $screen-lg - 30;
$container-xl: $screen-xl - 30;
$container-xxl: $screen-xxl - 30;
// Модульная сетка
$grid-columns: 12;
$grid-gutter-width: 30px;
// Разное
$border-radius: 3px;
$opacity: 0.7;
$transition-time: 0.3s;
$shadow: 0 4px 2px -2px rgba(0, 0, 0, 0.3);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment