I want to create an HTML web component and i need to import CSS file but I want to inject it into my shadow dom. I have some code like below:
import introHTML from './Intro.html';
import introCss from './Intro.css';
class IntroWebComponent extends HTMLElement{
constructor(){
super();
this.defineClassProp();
}
defineClassProp(){
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._html = introHTML
this._element = document.createElement('template');
this._element.innerHTML = this._html;
this._element.append(`<style>${introCss}</style>`)
this._shadowRoot.appendChild(this._element.content.cloneNode(true));
}
}
window.customElements.define('index-intro', IntroWebComponent);
but 'rollup-plugin-postcss' keep injecting css to my main html head and i dont know how to stop it
just config your rollup to stop inject style in your js module:
the default config is:
postcss({
inject: { insertAt: 'top' }
})
you must change it to:
postcss({
inject:false
})
Related
Problem:
I have built an angular application. There I want to add a CSS class to the body whenever It navigates to that components and remove it whenever the user leaves out of that component.
This is what I have done. In the global CSS file, I have defined a style like this.
.background-color {
background-image: linear-gradient(to right,#3f51b5, #00bcd4) !important;
}
And In the login component, I have done something like this.
import { Component, OnInit } from "#angular/core";
#Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.css"]
})
export class LoginComponent implements OnInit {
constructor() {
}
ngOnInit() {
window.onload = function() {
document.body.classList.add("background-color");
};
}
}
But this is not working correctly. When I navigate to this component the styles not added but when I refresh the pages it adds the styling to the body. I tried a lot to find a solution to this problem but I was unable to do so. Can someone help me to solve this issue? Thank you!
No need to write onLoad function inside the ngOnInit() method. I suggest you read the angular official ngOnit().
rewrite your ngOnInit() like below
ngOnInit() {
let element = document.getElementById("bg-color");
element.classList.add("background-color");
}
and your component.html file
<p id="bg-color">
Start editing to see some magic happen :)
</p>
Live Demo
Since you are using angular routing, this will not work unless you navigate to that component manually, because when you navigate to a router link, it does not reload the application, it justs removes the other components and add the new component. It does not trigger the onLoad event. But when you reload manually, it will work.
try the following
export class LoginComponent implements OnInit {
loaded: boolean = false;
constructor() {
}
ngOnInit() {
this.loaded = true;
}
and on the component html
<body [class.background-color]="loaded"></body>
or something on the similar lines.
I'm using create-react-app for a multi-language project.
I want to use some library like "cssJanus" or "rtlcss" to convert the Sass generated CSS file into a separate file and then use that newly generated file when I switch to another language.
Here's how my index.js looks like ...
import React from "react";
import ReactDOM from "react-dom";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
import { Provider } from "react-redux";
import App from "./App";
import { configureStore } from "./store/configureStore";
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById("root")
);
serviceWorker.unregister();
And here's how my "App.js" looks like ...
import React, { Component } from "react";
import "./App.scss";
import { Route, Switch } from "react-router-dom";
import SignIn from "./features/signin/SignIn";
class App extends Component {
render() {
return (
<>
<Switch>
<Route path="/" exact component={SignIn} />
</Switch>
</>
);
}
}
export default App;
As you can see I'm using "./App.scss" file that simply have a bunch of #import statements to another ".scss" files in the "./src/css/" directory ...
/* autoprefixer grid: on */
#import "css/reset";
#import "css/variables";
#import "css/global";
I need your advice on how to do that. How to convert the generated CSS from App.scss to RTL into their own .css file and switch between them and the original generated CSS based on a change in the global state.
I searched a lot for something like this but with no luck.
Or if you have a better approach I'm all ears.
Here is a simple solution that requires ejecting and adding a lightweight webpack-rtl-plugin.
After running
npx create-react-app react-rtl
cd react-rtl
yarn eject
yarn add -D webpack-rtl-plugin #babel/plugin-transform-react-jsx-source
Go to config/webpack.config.js and make some tweaks:
// import the plugin
const WebpackRTLPlugin = require('webpack-rtl-plugin')
// ...
module: { ... }
plugins: [
// ...,
// use the plugin
new WebpackRTLPlugin({ diffOnly: true })
].filter(Boolean),
// ...
On this stage, if you run yarn build and look up build/static/css folder, you should hopefully see additional .rtl.css file that contains your rtl styles.
Then we need to tell webpack to use MiniCssExtractPlugin.loader for development as well so it will serve styles through link tags instead of inline styles:
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && { loader: MiniCssExtractPlugin.loader }, // <-- use this
// isEnvDevelopment && require.resolve('style-loader'), <-- instead of this
and don't forget the plugin, lol:
module: { ... }
plugins: [
// ...,
// isEnvProduction && <-- comment this out
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
// ...
].filter(Boolean),
And from here you can finally grab your default stylesheet href and use to insert rtl styles. Here's how you could implement it:
class RtlCssBundleService {
constructor() {
this.rtlApplied = false
this.rtlStyles = [];
this.ltrStyles = Array.from(
document.querySelectorAll('link[rel="stylesheet"]')
)
}
insert = () => {
if (this.rtlApplied) { return }
this.rtlApplied = true
if (this.rtlStyles.length) {
return this.rtlStyles.forEach(style => {
document.body.appendChild(style)
})
}
this.rtlStyles = this.ltrStyles.map(styleSheet => {
const link = document.createElement("link")
link.href = styleSheet.href.replace(/\.css$/, '.rtl.css')
link.rel = "stylesheet"
document.body.appendChild(link)
return link
})
}
detach = () => {
this.rtlApplied = false
this.rtlStyles.forEach(style => {
document.body.removeChild(style)
})
}
toggle = () => {
return this.rtlApplied
? this.detach()
: this.insert()
}
}
const rtlStyles = new RtlCssBundleService()
export default rtlStyles
Then use this from any of your components.
So anyway, I'm sure I've missed something and maybe that is a terrible approach, but it seems to work and here is the demo
If you use flexbox and css grid they have RTL support built in. Then use CSS Logical Properties for margin, padding, border, etc. If that is not enough, then you can use [dir="rtl"] .your-class as a fallback.
Now you don't have two separate css files to maintain.
Here is a cross browser margin-right example.
-webkit-margin-end: 25px;
margin-inline-end: 25px;
#supports (not (-webkit-margin-end: 0)) and (not (margin-inline-end: 0)) {
margin-right: 25px;
}
You could wrap that up into a mixin for easier use across your app.
Looking around there is a library called react-with-direction from airbnb that provides a DirectionProvider - component you could wrap your components in based on the language. Hope that helps.
My project has two main parts. One for public web pages and the other for admin control panel. Each of them has separate CSS and javascript files for their template.
If I define all CSS and js files in index.html, all files load in the first meet of the web page, and also maybe have a conflict between CSS classes.
How can I handle this problem?
app.component:
<router-outlet></router-outlet>
app-routing.module:
import { NgModule } from "#angular/core";
import { Routes, RouterModule } from "#angular/router";
import { FirstComponent } from './first/first.component';
const appRoutes: Routes = [
{ path: 'first', component: FirstComponent },
{
path: 'controlpanel',
loadChildren: 'app/control-panel/control-panel.module#ControlPanelModule'
},
{
path: 'publicpanel',
loadChildren: 'app/public-panel/public-panel.module#PublicPanelModule'
}
];
each module has its submodules. Can I separate their styles?
Use sass and create a class flag for public and admin components
like this
theme/_public.scss
.public{
label {
color:red;
}
}
theme/_admin.scss
.admin {
label {
color:green;
}
}
and this in main style.scc
#import "theme/_public.scss";
#import "theme/_admin.scss";
this is much better for app performance you will have one style file with public and admin pages style
stackblitz example
I found the solution. We can disable or enable css files in component.
document.styleSheets[2].disabled = false;
or
document.styleSheets[2].disabled = true;
that's it.
I am using the following code for creating the dynamic components
import {
Component, OnInit, ViewContainerRef, ViewChild, ViewChildren,
ReflectiveInjector, ComponentFactoryResolver, ViewEncapsulation, QueryList, Input, AfterViewInit
} from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { forEach } from '#angular/router/src/utils/collection';
import { IComponent } from 'app/app.icomponent';
#Component({
encapsulation: ViewEncapsulation.None,
selector: 'dynamic-component',
entryComponents: [HomeComponent, HighlevelSignalComponent],
template: `
<div #dynamicDiv [ngClass]="classFromMenu" >
<ng-template #dynamicComponentContainer></ng-template>
</div>
`,
styleUrls: [
'./dynamic-content.component.css'
],
})
export class DynamicComponent implements IComponent, OnInit, AfterViewInit {
classFromMenu: any;
#ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver, private route: Router,
private activatedRoute: ActivatedRoute, ) {
}
.......
buildComponent(passedData) {
// orderAndObjs has the data for creating the component
this.orderAndObjs.forEach(obj => {
var componentFactory = this.resolver.resolveComponentFactory(obj.component);
var compRef = this.dynamicComponentContainer.createComponent(componentFactory);
// compRef is the component that is created.
//Assuming the component that i am trying to create is <dynamic-component>.
//I want to add either a class or any other attribute like this
//<dynamic-component class="flex">
});
}
}
}
The dynamic-component is created perfectly fine and everything is working as expected. But the only issue is I want to add a class for dynamic-component so that it can be
<dynamic-component class="dynamicClass">
Any help is appreciated :(
Hmm.. I usually add it to the selector of component that is supposed to be an entryComponent ...
selector: 'dynamic-component.someclass',
^^^^^^^^^^^
to add attribute use attribute selector:
selector: 'dynamic-component[myattr=value]',
I call it hidden feature of entryComponents
but its declarative approach and can't be changed at runtime(indeed we can change it)
In Angular 5/6, using Renderer2 from #angular/core, you can do something like below:
constructor(private resolver: ComponentFactoryResolver, private route: Router,
private activatedRoute: ActivatedRoute, private renderer2: Renderer2) {
}
buildComponent(passedData) {
this.orderAndObjs.forEach(obj => {
var componentFactory = this.resolver.resolveComponentFactory(obj.component);
var compRef = this.dynamicComponentContainer.createComponent(componentFactory);
this.renderer2.addClass(compRef.location.nativeElement, 'flex');
});
}
High-level DOM operations are performed with Renderer2 provider. Considering that it was injected, it is:
this.renderer2.addClass(compRef.location.nativeElement, 'dynamicClass');
It should be noticed that depending on how dynamic element is attached to DOM, this may be unnecessary complication.
Considering that dynamicComponentContainer is real DOM element and not <ng-template>, the view of dynamic component can be directly mounted to the container, thus eliminating <dynamic-component> wrapper element:
Given the container:
<div class="dynamicClass" #dynamicComponentContainer></div>
It will be:
var compRef = componentFactory.create(
this.injector,
[],
this.dynamicComponentContainer.element.nativeElement
);
I have a minimal React component which consists of two files: button.jsx and button.less. The styles are imported and the class names are appended with a hash to make all styles local to the component.
This is great, but i'd like to have all component code in one file. Is it possible to inline the styles in jsx file without losing css modularity?
Current Code
button.jsx
import React from 'react';
import styles from './button.less'
export default class Button extends React.Component {
render() {
return <button className={styles.primary}>{this.props.text}</button>;
}
}
button.less
#import '~semantic-ui/src/definitions/elements/button.less';
.common {
composes: ui button;
}
.primary {
composes: common primary;
}
webpack.config.js (relevant bits)
module: {
loaders: [
{
test: /\.jsx$/,
loader: 'babel'
},
{
test: /\.less$/,
loader: "style!css?modules&importLoaders=1!less"
}
]
},
What i'd like to write instead
button.jsx
<style lang="less" modules>
#import '~semantic-ui/src/definitions/elements/button.less';
.common {
composes: ui button;
}
.primary {
composes: common primary;
}
</style>
import React from 'react';
export default class Button extends React.Component {
render() {
return <button className={styles.primary}>{this.props.text}</button>;
}
}
Inspired by vue.js and vue-loader.
I believe this is a duplicate of this unanswered question:
Using css-loader inline with Webpack + React
I wrote a Webpack loader for this very purpose:
https://github.com/chrisdavies/stylextract-loader
It allows you to write a single style tag per JSX file and it supports webpack CSS modules, too, if you want.
At build time, it extracts the rules from your style tag and moves them out to an external CSS file.
I should note that because it simply extracts your rules out to an external CSS file, it plays nice with SASS, autoprefixer, etc
You can use callback-loader for this. This is actualy a workaround, but it do the trick. Just implement a callback which will extract your css-code and replace it with appropriate import. For example:
webpack.config.js
var fs = require('fs');
var cssIndex = 0;
// Do not forget to create and clean temporary folder "cssTemp" before
var webpackConfig = {
...
resolve: {
alias: {
cssTemp: path.resolve('./cssTemp')
}
},
module: {
loaders: [
{ test: /\.jsx$/, loader: "callback!babel" }
]
},
callbackLoader: {
cssCallback: function(code) {
var filename = cssIndex + '.less';
cssIndex++;
// Save the css code from the callback argument
fs.writeFileSync('./cssTemp/' + filename, code);
// Return the import statement which will replace the callback statement
return 'import styles from "cssTemp/' + filename + '";';
}
}
...
};
button.jsx
import React from 'react';
cssCallback(`
#import '~semantic-ui/src/definitions/elements/button.less';
.common {
composes: ui button;
}
.primary {
composes: common primary;
}
`);
export default class Button extends React.Component {
render() {
return <button className={styles.primary}>{this.props.text}</button>;
}
}