I am migrating from sass to postcss. To make it easier, I am using postcss-simple-vars, which supports similar variables like sass does.
My problem is:
$size: 100px;
.class {
width: $size * 2;
}
Sass result:
.class {
width: 200px;
}
Postcss with simple vars result:
.class {
width: 100px * 2;
}
The latter seems to be invalid css code. In order to fix it, I need to use the calc() function. The problem is to find all the occurances of those calculations in my code. I have ~ 1 MB of scss...
Is there any tool (linter, compiler, etc) which would analyze my css and warn me of all the places where this needs to be taken care of?
I'm not sure if there is a pre-build plugin ready to use. But you can create a simple task in gulp and point at any error on your css file.
create your new task in your in your gulp tasks and console.log the errorInfo from your style.css file using toString() method
etc
gulp.task('styles' function() {
return gulp.src('your src ./path for your style.css ')
.on('error', function(errorInfo) {
console.log(errorInfo.toString());
this.emit('end');
})
.pipe(gulp.dest('your dest ./path four final destination path '));
});
Updating
var processors = function() {
return function(css) {
css.eachDecl('width', function(decl) {
decl.value = decl.value + ', calc({{ size }} * 2)';
});
}
};
gulp.task('css', function() {
return gulp.src('./app/assets/styles/your_style.css')
.pipe(postcss([processors]))
.pipe(gulp.dest('./app/temp/styles/'));
});
Related
For example:
<script>
export default {
data() {
return{
varinjs: 1,
}
}
}
</script>
<style lang="stylus">
varincss = varinjs
body
if varincss == 0
background-color red
else varincss == 1
background-color blue
</style>
Is it any way to access javascript variables in css?can use sass or less, but I'd
like stylus much more.
I know this is not an answer to 'this' question (I wanted to comment) but I will try to give an alternate solution.
Stylus supports a built-in function json(path[, options]), which means you can put all variables into a JSON file and supply them both to your JS files as well as Stylus files.
You cannot access stylus variable using JS and you probably won't be able to do that unless you find some sort of build-time libraries that would convert specified js file/variable into stylus variables.
You could use CSS custom properties to achieve that.
Bind stylus variables with them and just handle changes on JavaScript.
<script>
export default {
data () {
return {
theme: { background: '#ff0000' }
};
},
watch: {
'theme.background': { immediate: true, handler: 'applyVariables' }
},
methods: {
applyVariables () {
const scope = document.documentElement.styles;
scope['--theme-background'] = this.theme.background;
}
}
};
</script>
<style lang="stylus">
theme-background = var(--theme-background, #ff054a);
// The first argument is variable name and second is placeholder value.
.theme-button
background-color: theme-background
</style>
CSS Custom Properties reference on MDN
I'm trying to implement a kind of css theming in an angular 4 project. We use webpack 3 for bundling. The product is intended to be used by several companies and has to look according to their brandbooks. So we need themes.
We gonna have several builds, but we don't want to have several versions of code. All themes should remain in the same codebase. The differences are minimal: colors, icons, fonts — everything may be changed in css.
I have thought of several ways to do it, the most obvious would be to implement theming via :host-context for components and change the class of body by changing environment variable for webpack. With such method we will heve every theme inside our bundle, which is not good. Maybe there's another way?
I wonder if it is possible to have webpack load not the css file it is asked for. Instead it could look for another file by pattern, and if it exists, use that file instead of original one. Or load both files.
For example, we have a button.component.ts which imports button.component.css. If we don't tell webpack to use any theme, it works as usual. But if we do, it tries to read button.component.theme-name.css in the same directory. If that file exists, webpack imports it instead (or altogether with) the default file.
That's basically what I'm trying to do. I guess, the same mechanism would be useful for html templates in angular.
Is there a plugin to do such magic? Or maybe some sophisticated loader option? If you have another way to solve my task — feel free to drop a comment!
I created a loader which can append or replace the content of a loaded file with the content of its sibling which has a chosen theme's title in its name.
TL;DR
Create a file with loader.
Use it in webpack config.
Run webpack in THEME=<themeName> evironment.
theme-loader.js
const fs = require('fs');
const loaderUtils = require('loader-utils');
module.exports = function (mainData) {
const options = loaderUtils.getOptions(this);
let themeName = options.theme;
let mode = options.mode;
if (themeName) {
// default mode
if (!Object.keys(transform).includes(mode)) {
mode = 'replace';
}
// fileName.suffix.ext -> fileName.suffix.themeName.ext
const themeAssetPath = this.resourcePath.replace(/\.([^\.]*)$/, `.${themeName}.$1`);
const callback = this.async();
// for HMR to work
this.addDependency(themeAssetPath);
fs.readFile(themeAssetPath, 'utf8', (err, themeData) => {
if (!err) {
callback(null, transform[mode](mainData, themeData));
} else if (err.code === 'ENOENT') {
// don't worry! if it's not here then it's not needed
callback(null, mainData);
} else {
callback(err);
}
});
} else {
return mainData;
}
};
const transform = {
// concat theme file with main file
concat: (mainData, themeData) => mainData + '\n' + themeData,
// replace main file with theme file
replace: (mainData, themeData) => themeData
};
A piece of sample webpack.config.js to use this handmade loader:
resolveLoader: {
modules: [
paths.libs, // ./node_modules
paths.config // this is where our custom loader sits
]
},
module: {
rules: [
// component styles
{
test: /\.css$/,
include: path.join(paths.src, 'app'),
use: [
'raw-loader',
// search for a themed one and append it to main file if found
{
loader: 'theme-loader',
options: {
theme: process.env.THEME,
mode: 'concat'
}
}
]
},
// angular templates — search for a themed one and use it if found
{
test: /\.html$/,
use: ['raw-loader',
{
loader: 'theme-loader',
options: {
theme: process.env.THEME,
mode: 'replace'
}
}
]
}
]
}
For example, an app.component.css:
:host {
background: #f0f0f0;
color: #333333;
padding: 1rem 2rem;
display: flex;
flex-direction: column;
flex: 1;
justify-content: center;
}
nav {
/* ... */
/* something about nav element */
/* ... */
}
header {
/* ... */
/* pile of styles for header */
/* ... */
}
To implement dark theme we don't need to change all that flex and padding staff and maybe nav and header don't have their own background and font color settings. So we'll just have to override host element style. We create app.component.dark.css:
:host {
background: #222222;
color: #e0e0e0;
}
The we run webpack with environment variable THEME set to dark. The loader takes a request to process app.component.css, tries to load app.component.dark.css and voila! Themed css is appended to the end of resulting file. Because of cascade,
if multiple competing selectors have the same importance and specificity, … later rules will win over earlier rules (MDN).
For HTML we don't have such method. So we'll have to rewrite our template completely. Hopefully, you won't need to do it too often. I my case, I wanted to change like header and footer to fit the cutomer's branding demand.
This was my first attempt to create a webpack loader, please leave a comment if you see a problem with it.
I am working on a JS Web App using multiple UI frameworks and Preprocessors like SASS,
I am using Gulp as a task runner but I am facing a strange behavior
for example:
in my sass file
.class_name{
text-align: left;
padding-left: 10px;
padding-right: 15px;
}
here is my gulp tasks
gulp.task('sass',function(){
gulp.src(['src/scss/base.scss',
'src/scss/**/**/*.scss'
]).pipe(concat('styles.scss'))
.pipe(sass())
.pipe(gulp.dest('src/css'))
})
gulp.task('styles',['sass'], function () {
return gulp.src(['src/css/styles.css',
'src/css/libs/**/*.css'
])
.pipe(concat('styles.css')) // Other post-processing.
.pipe(gulp.dest('public/css')) // Output LTR stylesheets.
.pipe(rtlcss()) // Convert to RTL.
.pipe(rename({ suffix: '-rtl' })) // Append "-rtl" to the filename.
.pipe(gulp.dest('public/css')); // Output RTL stylesheets.
});
gulp.task('watch',function () {
gulp.watch('src/scss/**/**/*.scss',['styles']);
})
gulp.task('default',['styles','watch']);
when I update anything in my sass file for example change line 3 to padding-left:5px;
the result in styles.css is still the old value paddin-left:10px;
if I change it on more time to padding-left:15px;
the result is padding-left:5px;
Why there is always one step delay in updating files !!!
Thanks in advance.
you should return result of saas
gulp.task('sass',function(){
return gulp.src(['src/scss/base.scss',
'src/scss/**/**/*.scss'
]).pipe(concat('styles.scss'))
.pipe(sass())
.pipe(gulp.dest('src/css'))
})
unless you return the style will not know when saas task finished
I want to write an empty file even the destination file is empty.
Is it possible?
The error I get is "destination not written because minified css was empty".
I'm using grunt-contrib-cssmin module.
Only using grunt-contrib-cssmin, you cannot.
A good place to start when wondering about something specific to a piece of open source software: the source code itself — the task is only ~70 lines. From the source we can see the "error" you're getting:
if (min.length === 0) {
return grunt.log.warn('Destination not written because minified CSS was empty.');
}
You might want to look into "touching" the file. If you are familiar with *nix, you'll know that touch will create the file if it doesn't exist and not truncate it if it does. In Grunt (node.js) you might want to look into node-touch or grunt-exec.
As for your Gruntfile, you'd need only one of the following two tasks:
module.exports = function (grunt) {
var touch = require('touch');
grunt.initConfig({
'cssmin': {
'combine': {
'files': { 'path/to/output.css': ['path/to/input_one.css', 'path/to/input_two.css'] }
}
},
'exec': {
'touch': { 'command': 'touch path/to/output.css' }
}
});
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-exec');
grunt.registerTask('touch', touch.sync.bind(undefined, 'path/to/output.css'));
grunt.registerTask('minifycss1', ['cssmin', 'exec:touch']); // 1
grunt.registerTask('minifycss2', ['cssmin', 'touch']); // 2
};
Uses grunt-exec.
Uses node-touch.
I have a scss variable $tint-color that is used in about 100 places.
Once the user logs in, I would like to load a color based on their profile and replace all the usages of $tint-color.
So far I have found two non-ideal solutions:
1) Iterate through all elements and replace the relevant properties.
I am constantly generating new elements -- so this would need to happen repeatedly.
2) Create an override stylesheet, that targets each element.
This will require a lot of duplicate code.
Is there a better / simpler way? I have thought about adding a class to an element in scss, but I am not sure this is possible. Thank you for your help in advance!
What I am doing now, is loading a theme css file after the profile is loaded.
On the server I expose an iron-router route that dynamically replaces any occurrence of the color and returns the theme css.
The issue is that I am not replacing the scss variables, instead I am replacing any occurrence of the color. This is because when the code is executed the .scss files have already been bundled into a .css file on the server.
// return a theme based on the tintColor parameter
this.route('theme', {
where: 'server',
action: function () {
var files = fs.readdirSync('../client');
// find the css file (not the .map file)
var cssFile = _(files).find(function (fileName) {
return fileName.indexOf('.css') > 0 && fileName.indexOf('.map') < 0;
});
var style = fs.readFileSync('../client/' + cssFile, 'utf8');
// remove comments (cannot have them for minification)
style = style.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '');
// replace the default tint-color with the dynamic color
style = style.replace(/8cb850/g, this.params.tintColor);
// minify css
if (Settings.isProduction()) {
// from the minifiers package
style = CssTools.minifyCss(style);
}
this.response.writeHead(200, {'Content-Type': 'text/css'});
this.response.end(style);
}
});
Update: I got it to generate with scss variables.
Theme.compile = function (tintColor) {
var dirName = path.dirname(styleFile);
var styles = fs.readFileSync(styleFile, 'utf8');
//replace default theme with dynamic theme
var theme = '$tint-color: #' + tintColor + ';' + '\n';
styles = styles.replace('#import "app/theme.scssimport";', theme);
var options = {
data: styles,
sourceComments: 'map',
includePaths: [dirName] // for #import
};
var css = sass.renderSync(options);
// minify css
if (Settings.isProduction()) {
// remove comments -- cannot have them for minification
css = css.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '');
// Use CssTools from the minifiers package
css = CssTools.minifyCss(css);
}
return css;
};
If you do this make sure you add the scss files as assets in the package, example here.
Set a basic $tint-color in your original css.
Then use meteor to send inline CSS with the selected user-tint.
Example:
.tint {
background-color: USER-TINT;
color: USER-TINT;
}
That way you can cache the original css file and save loads of transfer!