I'm using critical to generate Critical path CSS for my site. I have a gulp task set up as follows:
gulp.task('critical-css', function () {
critical.generate({
base: 'CriticalCss/',
inline: false,
src: 'index.html',
dest: 'critical.min.css',
minify: true,
width: 320,
height: 480
}).then(function (output) {
// Take 'output' and place it in masterpage
});
});
I'm wondering if it's possible to take the value of the output parameter, which is my Critical CSS, and inject this into my Masterpage through gulp inside of an inline <style> tag?
I've tried using my Masterpage as the dest property however this doesn't work as the source file is different due to it being made up of the server generated HTML. I cannot use the Masterpage as the source file either as it's not a true representation of what is rendered to the user for obvious reasons.
Would something like html-replace work for this?
So I managed to do this with gulp-inject, it allows you to dynamically inject files or file contents into your pages pretty easily.
Here's my final gulp task:
gulp.task('critical-css', function () {
critical.generate({
base: 'CriticalCSS/',
inline: false,
src: 'index.html',
dest: 'critical.min.css',
minify: true,
width: 320,
height: 480
})
.then(function (output) {
gulp.src('./CMSTemplates/Web/MasterPages/Root.Master')
.pipe(inject(gulp.src(['./CriticalCSS/*.css']), {
starttag: '<!-- inject:{{path}} -->',
transform: function (filePath, file) {
return '<style type="text/css">'
+ output +
'</style>';
}
}))
.pipe(gulp.dest('./CMSTemplates/Web/MasterPages'));
});
});
It ends up replacing this:
<!-- inject:/CriticalCSS/critical.min.css-->
<!-- endinject -->
With this:
<!-- inject:/CriticalCSS/critical.min.css-->
<style type="text/css">#charset "UTF-8";img{max-width:100%}img{border:0}body{padding:0}img{height:auto}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;box-sizing:border-box}header,nav{display:block}img{-ms-interpolation-mode:bicubic;display:inline-block;vertical-align:middle}input{color:inherit;font:inherit;margin:0}body{-webkit-font-smoothing:antialiased}a,i{line-height:inherit}ul{line-height:1.6}input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}.foundation-mq{font-family:"small=0em&medium=40.125em&large=64.125em&xlarge=75em&xxlarge=90em"}*,::after,::before{box-sizing:inherit}body{margin:0;-moz-osx-font-smoothing:grayscale}.clearfix::after{clear:both}div,form,li,ul{margin:0;padding:0}ul{margin-left:1.25rem}ul{margin-bottom:1rem}i{font-style:italic}a{background-color:transparent;text-decoration:none}a img{border:0}ul{list-style-position:outside}li{font-size:inherit}ul{list-style-type:disc}.menu{list-style-type:none}.button{text-align:center}.menu>li,.menu>li>a i,.menu>li>a i+span{vertical-align:middle}.button,.menu>li>a{line-height:1}.button{-webkit-appearance:none;border-radius:0;margin:0 0 1rem;background-color:#2199e8}.menu a{margin-bottom:0}.menu{margin:0}.menu>li>a{display:block;padding:.7rem 1rem}.menu>li>a i{margin-right:.25rem;display:inline-block}.menu>li{display:table-cell}.menu.vertical>li{display:block}body,html{height:100%}#media screen and (max-width:64.0625em){.show-for-large{display:none!important}}.clearfix::after,.clearfix::before{content:' ';display:table}body,html{background:#eeedea}body,html{color:#000;letter-spacing:0}body,html{font:100%;font-family:"Trebuchet MS";font-size:100%;font-style:normal;line-height:1.5rem}a{color:#000}.header__wrap .menu a,.header__wrap .menu span{font-family:ApercuProBold,"Trebuchet MS"}#font-face{font-family:ApercuProBold;src:url(/web/css/fonts/ApercuPro-Bold-Web.eot);src:url(/web/css/fonts/ApercuPro-Bold-Web.eot?#iefix) format("embedded-opentype"),url(/web/css/fonts/ApercuPro-Bold-Web.woff2) format("woff2"),url(/web/css/fonts/ApercuPro-Bold-Web.woff) format("woff"),url(/web/css/fonts/ApercuPro-Bold-Web.ttf) format("truetype"),url(/web/css/fonts/ApercuPro-Bold-Web.svg#ApercuPro-Bold) format("svg");font-weight:700;font-style:normal}#font-face{font-family:icomoon;src:url(/web/css/fonts/icomoon/icomoon.eot?h3styg);src:url(/web/css/fonts/icomoon/icomoon.eot?h3styg#iefix) format("embedded-opentype"),url(/web/css/fonts/icomoon/icomoon.ttf?h3styg) format("truetype"),url(/web/css/fonts/icomoon/icomoon.woff?h3styg) format("woff"),url(/web/css/fonts/icomoon/icomoon.svg?h3styg#icomoon) format("svg");font-weight:400;font-style:normal}.header__menu .destinations-menu-item:after{content:""!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.header{z-index:999;width:100%;height:45px;position:fixed;background:#000}.header__wrap{position:relative;height:100%}.header__section{width:auto;display:inline-block;height:100%}.header__wrap .menu a{padding-right:.5rem;padding-left:.5rem;text-decoration:none}.header__wrap .menu a,.header__wrap .menu span{color:#fff;font-size:.875rem;letter-spacing:0}.header__logo{position:relative;top:10px;float:left}.header__logo .logo{display:block}#media only screen and (min-width:40.063em) and (max-width:64em){.header__logo{width:60px}}.header__menu{position:relative;width:100%;height:100%}.header__menu ul.menu{position:absolute;display:none}.header__menu .parallel-logo>i{display:none}.header__menu .parallel-logo>span{text-indent:100%;white-space:nowrap;overflow:hidden;margin-left:10px;background-image:url(/web/images/logos/logo_parallel.svg);background-repeat:no-repeat;width:62px;height:13px;display:inline-block;position:relative;top:1px}.header__menu .destinations-menu-item{position:relative;padding-right:12px!important;margin-right:12px!important;text-decoration:none}.header__menu .destinations-menu-item:after{font-family:icomoon;font-size:.75rem;color:#fff;position:absolute;top:13px;right:-3px;display:inline-block;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}#-moz-document url-prefix(){.header__wrap .parallel-logo{padding-top:14px}.header__menu .destinations-menu-item:after{top:15px}}#media only screen and (min-width:40.063em) and (max-width:64em){.header__menu .destinations-menu-item:after{top:11px}}#media only screen and (min-width:40.063em) and (max-width:64em){.header__menu{height:90px;width:100%}}#media (min-width:1024px) and (max-width:1200px){.header__menu ul.menu{padding-left:10px}}.button{-webkit-transform:perspective(1px) translateZ(0);box-shadow:0 0 1px transparent}.form .error{border:2px solid #e85460!important}.button:after{content:""!important;line-height:inherit}.button{color:#000;display:inline-block;overflow:hidden;letter-spacing:0;vertical-align:middle}.button,.button:after{font-size:.875rem}.button{background:#f7e066;font-family:ApercuProBold,"Trebuchet MS";padding:17px 24px 17px 20px;margin-bottom:.3125rem;border:none;text-transform:uppercase;transform:perspective(1px) translateZ(0);position:relative}.button:after{font-weight:400;text-transform:none;speak:none;font-style:normal;font-variant:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.button:after{font-family:icomoon;color:#000}.button:before{content:"";z-index:-1;height:3px;left:0;bottom:0}.button:before{position:absolute;right:100%;background:#000}.button:after{position:absolute;right:19px;height:14px;top:50%;margin-top:-7px}.newsletter-modal span.error{color:#fff;background:#e85460;padding:12px 0 12px 40px;line-height:1;position:relative;width:100%;float:left;margin-bottom:20px}.newsletter-modal span.error::before{content:'!';width:20px;height:20px;display:block;position:absolute;border:2px solid #fff;border-radius:50%;text-align:center;left:12px;top:10px;font-size:15px}select.uppercase option{text-transform:uppercase}#destinationDropdown.destination-dropdown-menu{z-index:99999;background:#eeedea;color:#000;position:absolute;top:90px;left:22px;padding:15px;display:none;border:1px solid #ddd}#destinationDropdown.destination-dropdown-menu ul{padding:0;margin:0;top:0}#destinationDropdown.destination-dropdown-menu li{outline:0}#destinationDropdown.destination-dropdown-menu li a{text-transform:uppercase;color:#000;font-family:ApercuProBold,"Trebuchet MS";font-size:1.125rem;outline:0;padding:0}#destinationDropdown.destination-dropdown-menu .button{margin-bottom:0;margin-top:15px;width:100%;text-align:left}#destinationDropdown.destination-dropdown-menu .menu-item{margin-bottom:5px}#destinationDropdown.destination-dropdown-menu .menu-item span{color:#000;font-size:20px;padding:0 7px}#stickyForm .select select option{color:#000}#stickyForm .form input{width:100%;margin-bottom:.3125rem}</style>
<!-- endinject -->
In my Masterpage file, hopefully this helps someone else.
Related
No fancy webpack, simple Vue custom element with some global css and some inline css for overrides.
I would like to use some styling library, like from getbootstrap.com and have it change styles inside custom element.
https://jsfiddle.net/Deele/6xk1atrn/25/
<div class="btn bg-info">Zero</div>
<test-widget id="One"></test-widget>
<test-widget id="Two"></test-widget>
const TestWidget = Vue.defineCustomElement({
props: {
id: String
},
data: () => {
return {
message: 'Test'
}
},
emits: {},
template: `<div class="btn bg-info">{{id}} {{message}}</div>`,
styles: [`div { color: green; }`]
})
customElements.define('test-widget', TestWidget)
.bg-info {
background-color: red!important;
}
Was expecting divs inside rendered elements would be styled as buttons, but it does not work!?
From what I have found in the internet, it has something to do with Shadow DOM not inheriting any global styles.
Please, tell me if there is a solution to this approach? I would like to create small widgets for my website using Vue.js, but this hurdle creates fatal limitation.
Custom elements defined using the Vue API always use a shadow DOM, so they are isolated from the parent document and any global styles in the app.
So to make it happen, You can inject the bootstrap styles or any global style url's in the styles option by using #import statement.
Live Demo :
const TestWidget = Vue.defineCustomElement({
props: {
id: String
},
data: () => {
return {
message: 'Test'
}
},
template: `<div class="btn bg-info">{{id}} {{message}}</div>`,
styles: [`#import url("https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta.2/css/bootstrap.css"); div { color: green; }`]
});
customElements.define('test-widget', TestWidget);
<script src="https://unpkg.com/vue#next"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta.2/css/bootstrap.css"/>
<div class="btn bg-info">Zero</div>
<test-widget id="One"></test-widget>
<test-widget id="Two"></test-widget>
I am trying to use the NPM React module for Mailchimp: https://www.npmjs.com/package/react-mailchimp-form. It works great and gives you all the forms that you may need but I am struggling to style it.
It says that you can add a personalized class for CSS styling, but how will that contain styles for all elements on the form?
Currently the form looks like this:
function MailchimpForm() {
return (
<div>
<Mailchimp
action=
fields={[
{
name: 'EMAIL',
placeholder: 'Email',
type: 'email',
required: true
},
{
name: 'COMPANY',
placeholder: 'name',
type: 'text',
required: true
}
]}
// Change predetermined language
messages={
{
sending: "Sending...",
success: "Thank you for subscribing!",
error: "An unexpected internal error has occurred.",
empty: "You must write an e-mail.",
duplicate: "Too many subscribe attempts for this email address",
button: "Subscribe!"
}
}
// Add a personalized class
className='MailchimpStyle'
/>
</div>
)
}
Where MailchimpStyle is my CSS style class. Is there a way to have multiple CSS styles in a class?
The current class looks like:
.MailchimpStyle {
clear: left;
font: 200px Helvetica, Arial, sans-serif;
}
You should be able to access the elements by specifying the elements after the className in the css sheet. Example for button:
.MailchimpStyle button {
clear: left;
color: black;
background-color: white;
margin: 2px;
}
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 have one requirement. Can you anybody suggest the possible ways to achieve it.
I want to change the theme of my application based on the URL passed in every routes.I am using the below technologies.
- Frontend : Angular JS
- Backend : Node.js
eg: localhost/x/about
localhost/y/about
I achieved these via cookies by passing the param using localtion.search at the time of login. But I need that theme param in all the routes.Based on that theme need to change.Can anyone suggest the possible ways.
app.js
app = angular.module('themeApp', ['ngRoute'])
app.config(function($routeProvider){
$routeProvider
.when('/',{
templateUrl: 'home.html'
})
.when('/:id/about',{
templateUrl: 'about.html'
})
.otherwise({
redirectTo: '/'
});
});
app.controller('themeController', function ($scope, $routeParams) {
console.log($routeParams.id);
// set the default theme
$scope.css = $routeParams.id;
});
menu.html (it is not complete, confuse here. please correct, how to call)
<li>
About
</li>
<li>
Home
</li>
index.html
<html ng-app="themeApp" ng-controller="themeController">
<head>
<!-- pull in css based on angular -->
<link rel="stylesheet" ng-href="css/{{css}}.css">
</head>
<body>
<div ng-view></div>
<!-- bring in JS and angular -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.14/angular.js"> </script>
<script rc="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.14/angular-route.js"></script>
<script src="app.js"> </script>
</body>
</html>
css/
it contains the three files,
- red.css
body{
background-color: red;
color: #333;
font-family: "Source Sans Pro",Calibri,Candara,Arial,sans-serif;
font-size: 15px;
}
green.css
body{
background-color: green;
color: #333;
font-family: "Source Sans Pro",Calibri,Candara,Arial,sans-serif;
font-size: 15px;
}
blue.css
body{
background-color: blue;
color: #333;
font-family: "Source Sans Pro",Calibri,Candara,Arial,sans-serif;
font-size: 15px;
}
The quick and simple answer would be to use $rootScope. $rootScope is available to all controllers, but like all global variables should be used sparingly.
Here's a good post explaining $rootScope. http://www.dotnet-tricks.com/Tutorial/angularjs/UVDE100914-Understanding-AngularJS-$rootScope-and-$scope.html
Basically if you're using it on your two controllers for pages x and y
app.controller('login',['$scope','$rootScope', function ($scope,$rootScope) {
$rootScope.foo='bar';
}]);
app.controller('xCtrl',['$scope','$rootScope', function ($scope,$rootScope) {
$scope.lorem=$rootScope.foo;
console.log($scope.lorem); //bar
}]);
app.controller('yCtrl',['$scope','$rootScope', function ($scope,$rootScope) {
$scope.ipsum=$rootScope.foo;
console.log($scope.ipsum); //bar
}]);
Can then use them in your HTML markup as normal.
Using $rootScope is much simpler, but if you wanted to use the router to pull down the id each time, it is also possible.
app.config(function($routeProvider){
$routeProvider
.when('/',{
controller: 'indexController',
templateUrl: 'view/index.html'
})
.when('/:id/about',{
controller: 'xCtrl',
templateUrl: 'view/x.html'
})
.otherwise({
redirectTo: '/'
});
});
app.controller('xCtrl',['$scope','$routeParams', function($scope,$routeParams){
$scope.foo=$routeParams.id;
}]);
If you have a lot more pages than just /about I could imagine this getting a bit tricky with the routing.
You could even couple it with root scope then pass it around to your other controllers.
app.controller('xCtrl',['$scope','$rootScope','$routeParams', function($scope,$rootScope,$routeParams){
$rootScope.foo=$routeParams.id;
$scope.lorem=$rootScope.foo;
}]);
--EDIT based on comment--
I might need some code to be sure of what you're after, but perhaps this clarifies?
URL: mysite.com/blue/about
app.config(function($routeProvider){
$routeProvider
.when('/',{
controller: 'indexController',
templateUrl: 'view/index.html'
})
.when('/:id/about',{
controller: 'xCtrl',
templateUrl: 'view/x.html'
})
.otherwise({
redirectTo: '/'
});
});
app.controller('xCtrl',['$scope','$routeParams', function($scope,$routeParams){
$scope.theme=$routeParams.id;
}]);
HTML
<div ng-controller="xCtrl">
<div ng-class="{{theme}}">
My theme is {{theme}}.
</div>
</div>
CSS
.blue
{
background-color: blue;
}
.green
{
background-color: green;
}
I am trying to figure out how to dynamically change a size of ngDialog that I use for my popups. The dialog fires event when it's been opened:
$scope.$on('ngDialog.opened', function (e, $dialog) {
dialogReady($dialog);
});
I tried all of these:
function dialogReady(caseEditorWindow) {
caseEditorWindow.css({ 'width' : '1350px' });
caseEditorWindow.width(1350);
caseEditorWindow.css('width', '1350px' });
}
Nothing takes any effect.
It only gets sized if I use a class like this:
<style>
.ngdialog.dialogcaseeditor .ngdialog-content
{
width : 1350px;
height: 810px;
margin-top:-100px;
padding-top:10px;
}
</style>
at the time of creating the dialog:
ngDialog.open({
template: 'caseEditor.html',
controller: 'caseEditorController',
className: 'ngdialog-theme-default dialogcaseeditor',
closeByDocument: false,
disableAnimation: true,
scope: $scope
data: row
})
Any idea?
Thanks
var dialog = ngDialog.open(); //always return object {id: 'div attr id'};
$('#'+ dialog.id).css('width', '100px'); //You can get outer div like this
Althrough, not so good using jquery in angular controller, but it works (the only way).