How to use Material Design Icons in a web component? - web-component

Looks like that mdi is not working inside web components, or do I miss something?
I want to develop a web component that encapsulates it's dependencies, adding the link to the parent document works, but it violates the original intent.
<html>
<body>
<x-webcomponent></x-webcomponent>
<script>
customElements.define(
"x-webcomponent",
class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>#import url('https://cdn.materialdesignicons.com/4.9.95/css/materialdesignicons.min.css');</style>
<span class="mdi mdi-home"></span>
`;
}
}
);
</script>
</body>
</html>
https://codepen.io/Jamesgt/pen/MWwvJaw

The #font-face CSS at-rule for the font you want to use must be declared in the main document, not in the Shadow DOM.
Because in your case it is defined in the materialdesignicons.min.css file, you'll need to load it in the main document via a global <link>.
Note that the CSS file won't be loaded twice thanks to the browser's cache.
Alternately, you could add it in the light DOM of the web component, or you could just declare the #font-face at-rule (copied from the materialdesignicons.css file).
Here is a running example:
customElements.define( "x-webcomponent", class extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: "open" })
this.shadowRoot.innerHTML = `
<link rel=stylesheet href=https://cdn.materialdesignicons.com/4.9.95/css/materialdesignicons.min.css>
<span class="mdi mdi-home"></span>`
}
connectedCallback () {
this.innerHTML = `<style>
#font-face {
font-family: "Material Design Icons";
src: url("https://cdn.materialdesignicons.com/4.9.95/fonts/materialdesignicons-webfont.woff?v=4.9.95") format("woff");
}
</style>`
}
} )
<x-webcomponent></x-webcomponent>

Related

Use bootstrap css and other global css in Vue.js custom elements

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>

How to use Flatpickr in Stenciljs components?

Flatpickr input field is not showing up in the stencil component with proper css.
I added the flatpickr date input field in a newly created (using stencil cli) app. No other settings or configs are changed.
import { Component, h } from '#stencil/core';
import flatpickr from 'flatpickr';
#Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true,
})
export class MyComponent {
private element: HTMLInputElement;
componentDidLoad() {
flatpickr(this.element, {
});
}
render() {
return (
<div>
<input ref={el => this.element = el} type="text" id="flatpickr" />
</div>
)
}
}
I'm guessing the problem is with the styling since the code you posted looks correct.
Flatpickr appends the calendar to the body element by default and since CSS is encapsulated when ShadowDOM is enabled (shadow: true) the styles in my-component.css won't affect it.
I see three options:
1. Append to different element
You can set a different parent for the calendar (your component or any element in it)
import { Component, Element, h } from '#stencil/core';
// ...
export class MyComponent {
#Element() el: HTMLElement;
private element: HTMLInputElement;
componentDidLoad() {
flatpickr(this.element, {
appendTo: this.el,
});
}
render() {
return (
<div>
<input ref={el => this.element = el} type="text" id="flatpickr" />
</div>
)
}
}
And import the styles in the CSS (my-component.css):
#import '~flatpickr/dist/flatpickr.min.css';
2. Include the Flatpickr CSS globally.
Include the CSS in your HTML head or any global CSS file.
3. Disable ShadowDOM
Set shadow: false to allow the styles in my-component.css to affect elements outside your component and import the CSS in my-component.css (same as in Option 1.).

How to import CSS file content into a Javascript variable

Consider a very simply custom element using shadow DOM:
customElements.define('shadow-element', class ShadowElement extends HTMLElement {
constructor() {
super();
this.styleTag = document.createElement('style');
this.styleTag.textContent= `
.root::before {
content: "root here!";
color: green;
}
`
this.shadow = this.attachShadow({mode: 'closed'});
this.root = null;
}
connectedCallback() {
this.root = document.createElement('div');
this.root.className = 'root';
this.shadow.append(this.root, this.styleTag);
}
})
<shadow-element></shadow-element>
To get the CSS into the shadow DOM, I create a style tag, which I append into the shadow root. This is all working fine so far.
Now for more complex CSS I would like to author it in a file shadow-element.css which is in the same folder as shadow-element.js. Besides seperation of concerns I also want IDE syntax highlighting and -completion for CSS authoring, so I really want the CSS in a separate, dedicated file.
I want to import the contents of that CSS file into a Javascript variable, like
import styles from './shadow-element.css'; // obviously doesn't work
On the project where this is being used we have a working webpack stack that allows importing CSS (and even SCSS), but unfortunately that imported CSS then becomes part of bundle.css - which obviously is not at all useful, because the element uses shadow DOM.
Does anyone have a solution to this? I'm also open to alternative solutions, as long it won't require me to author my CSS in a .js file.
Edit: I am aware of the option of using #import './shadow-elements.css'; inside the style tag, but I would much prefer a solution that bundles the imported CSS into my Javascript bundle (as part of the component code).
As you are using webpack, you can use raw-loader to import a text file (CSS in your case) into a string:
npm install raw-loader --save-dev
And you can use it inline in each file:
import css from 'raw-loader!./shadow-element.css';
customElements.define('shadow-element', class ShadowElement extends HTMLElement {
constructor() {
super();
this.styleTag = document.createElement('style');
this.styleTag.innerText = css;
this.shadow = this.attachShadow({mode: 'closed'});
this.root = null;
}
connectedCallback() {
this.root = document.createElement('div');
this.root.className = 'root';
this.shadow.append(this.root, this.styleTag);
}
})

Cant overwrite the custom stylesheet

I am trying to overwrite the CSS of react range slider.It uses the custom style sheet of which i need to add in the head section.My project is built on next.js
<link rel="stylesheet" href="https://unpkg.com/react-rangeslider/umd/rangeslider.min.css" />
Otherwise, the slider doesn't show anything if i don't add the link in head even though I installed the library. It's not even overwriting the CSS. I want to change the background color.This is my code:
import React, { Component } from 'react'
import 'react-rangeslider/lib/index.css';
import './slider.css';
import Slider from 'react-rangeslider'
class Horizontal extends Component {
constructor (props, context) {
super(props, context)
this.state = {
value: 850
}
}
handleChangeStart = () => {
console.log('Change event started')
};
handleChange = value => {
this.setState({
value: value
})
};
handleChangeComplete = () => {
console.log('Change event completed')
};
render () {
const { value } = this.state
return (
<div>
<div className='slider' style={{ marginTop:'165px',marginLeft:'319px',width:'700px',backgroundColor:'EF5350'}} >
<div style={{ textAlign:'center',color:'gray',fontSize:'35px',marginBottom:'82px'}}>
<p> What is the size of your property?</p>
</div>
<Slider
min={850}
max={5000}
value={value}
onChangeStart={this.handleChangeStart}
onChange={this.handleChange}
onChangeComplete={this.handleChangeComplete}
/>
<div className='value'>{value}</div>
</div>
</div>
)
}
}
export default Horizontal
I tried to change the background color in slider.css.
.rangeslider-horizontal .rangeslider__fill {
background-color: red;
}
The library needs to be installed first:
npm install react-rangeslider --save
It doesn't work as the slider stylesheet overwrite yours. Include the style like
// To include the default styles
import 'react-rangeslider/lib/index.css'
// import your css
import './style.css';
Demo
always make your own CSS stylesheet file the last file to import after any other CSS stylesheet files to make overwrite you need
otherwise, you can always use the console in the browser to auto-detect any error by pressing F12 in the browser then go to the tab called (console)
I think you can style element you want to live in the console to know the detail of how to nesting element
you also can open the CSS file in the editor and press Ctrl+F then find the line of code you want to style then copy its property and value to your own CSS file and then you can edit it so easy

Overlapping CSS in React, Webpack application

I have created an application using webpack and reactjs.
So far I have created 2 pages. I have defined CSS styling for both the pages. But when I load page 2 after loading page 1, the styles from page 1 are interfering with those of page 2.
For example
Page 1
require('style1.css');
var Page1 = React.createClass({
render: function(){
return(
<div> <h1>This is Page1</h1> <span> hello from page1</span></div>
)
}
});
module.exports = Page1;
style1.css
span {
color : red
}
Page 2
require('style2.css');
var Page2 = React.createClass({
render: function(){
return(
<div> <h1>This is Page2</h1> <span> hello from page2</span></div>
)
}
});
module.exports = Page2;
style2.css
h1 {
color : blue
}
When page2 is loaded after page1, the color of span was red, which was loaded from page1's style. Is there any way to avoid such kind of interferences or am I doing something wrong here?
You can have local stylesheets for each React component.
So the style sheet itself will have something like this:
:local(.styles) {
.your-style{...}
}
You can store it in the same folder as your component code. You import the style like so:
/* component styles */
import { styles } from './styles.scss'
In the render function of your component you will have this:
return (
<div className={styles}>
...
</div>
)
Everything within that <div> will have the stylesheet applied.
Loader configuration for your Webpack:
loaders: [{
test: /\.scss$/,
loader: 'style!css?localIdentName=[path][name]--[local]!postcss-loader!sass',
}]
You can look at this awesome boilerplate app, that implements all of this very nicely.
Webpack is not going to fix the inherent problems with style sheets. If you want component level styling the simplest solution is to go with inline styles. You might also look at Radium. https://github.com/FormidableLabs/radium

Resources