I have a button web component built that I am trying to style using CSS parts. In my web component below you can see that the button is colored tomato because I have used exportparts and then in my consumer CSS I have styled it using btn-comp::part(btn) { ... }.
However, I would prefer to not have to style it using btn-comp, instead I would like to just use :root to style it like this author does.
For example I should be able to do this:
:root::part(btn) { /* my styles here */ }
But that does not work, it only works when I use the component name. How can I style my CSS parts using :root?
const template = document.createElement('template');
template.innerHTML = `<button part="btn">I should be a green button</button>`;
class BtnComp extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(template.content.cloneNode(true));
const button = this.shadowRoot.querySelector("button");
button.addEventListener("click", () => alert('Clicked'));
}
}
window.customElements.define('btn-comp', BtnComp);
:root::part(btn) {
background-color: green !important; /* I want this color to be applied! */
}
btn-comp::part(btn) {
background-color: tomato;
color: white;
padding: 1rem 2rem;
border: 0;
cursor: pointer;
}
<btn-comp exportparts="btn" />
Related
I'm trying to make different icons in the footer, with different brands. I want them to change color when I'm hovering over them. I've created a CSS class with the hover pseudo-class, but I want to make a sort of parameter in my JSX file which tells the class that a certain color should be applied to a certain icon
This Is my CSS class:
.icon-background {
color: rgb(49, 45, 44, 0.8);
}
.icon-background:focus,
.icon-background:hover {
background-color: var(--color);
transition-duration: 0.2s;
padding: 2.5px;
}
An easy way to accomplish this would be through utility css classes. For instance, you could insert an icon in the jsx file like this:
<div className="blue default_icon">ICON</div>
With corresponding css:
.blue:hover,
.blue:focus {
background-color: #0000ff;
}
.default_icon {
color: rgb(49, 45, 44, 0.8);
}
.default_icon:hover {
transition-duration: 0.2s;
padding: 2.5px;
}
A better way you could do this is with React state. Basically, create an icon component and pass a color as a prop. This will be much less css and will be more scalable:
import { useState } from 'react';
const Icon = (props) => {
const hoverColor = props.hoverColor;
const [isHover, setIsHover] = useState(false);
const handleMouseEnter = () => {
setIsHover(true);
};
const handleMouseLeave = () => {
setIsHover(false);
};
const hoverStyle = {
backgroundColor: isHover ? hoverColor : "defaultColor",
};
return (
<div
style={hoverStyle}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
ICON
</div>
);
};
export default Icon;
You can then use the icon anywhere in your project like so:
<Icon hoverColor={"#0000ff"} /> // This would turn blue on hover
I am passing various amounts of data in react markdown sich as tables, lists and h tags. I wanted to know how to style each element separately. I searched everywhere but there is no mention of how to style the elements other than just bold or emphasize, etc. I thought I could pass a class name in the ReactMarkdown component to style it but would that not just provide styling only for the component. How do I style the various elements within it?
const ReadMePreviewer = ({ readMeCode }) => {
return (
<ReactMarkdown
plugins={[[gfm, { singleTilde: false }]]}
className={style.reactMarkDown}
// className={style.reactMarkDownRemovingExtraGlobal}
renderers={renderers}
source={readMeCode}
/>
);
};
This is how I would make it work for me. I do it explicit with CSS and not, e.g., SCSS or CSS-in-JS so as not to bother you with extra libraries nor webpack/babel finetuning:
Create a separate markdown-styles.module.css CSS-module file with your styles, e.g.,
.reactMarkDown {
// some root styles here
}
.reactMarkDown ul {
margin-top: 1em;
margin-bottom: 1em;
list-style: disc outside none;
}
.reactMarkDown ul li {
margin-left: 2em;
display: list-item;
text-align: -webkit-match-parent;
}
// your other styles but all under .reactMarkDown blocks
Then you can import it in your component and use as you did:
import style from './markdown-styles.module.css';
...
const ReadMePreviewer = ({ readMeCode }) => {
return (
<ReactMarkdown
plugins={[[gfm, { singleTilde: false }]]}
className={style.reactMarkDown}
renderers={renderers}
children={readMeCode}
/>
);
};
This seem to work to wrap into new line
App.css
.markdown code {
display: block;
white-space: pre-wrap;
}
Component:
<ReactMarkdown className="markdown">{page.Content}</ReactMarkdown>
I'm using snackbar in my Angular 9 project but some reason I'm getting the same background and text color for action button. I added css style in the component.scss file and also global style.scss file but still not working.
this._snackBar.open(`Chart has been copied to ${workspace.name}`, 'Check it out!',
{ duration: 5000, panelClass: "blue-snackbar"}).onAction().subscribe(() => {})
I also tried like this
panelClass: ['my-snack-bar', 'button-bar']}
I also add this in styles.scss and component.scss file but still not working
.my-snack-bar {
background-color: #E8EAF6;
color: red;
}
.button-bar {
background-color: pink;
color: blue;
}
example
Not sure why it's happening..any suggestion or help will be really appreciated.
In your global styles file, usually styles.scss you need to target the panelClass property, something like this:
.my-snack-bar {
background: #2196F3;
button {background:white; color: blue}
}
Here's a working example https://stackblitz.com/edit/angular-stacb4-b1fuu5
Is there a way to inherit :host element css styles into shadow DOM?
The reason is if we start developing web components, each web component style must be consistent on a page.
The page can have global css, and this global css styles can be inherited to shadow DOM. There was ::shadow and /deep/, but it's deprecated now.
Or, is this against pattern? If so, why?
I found this Q/A, but seems outdated for me.
Can Shadow DOM elements inherit CSS?
http://plnkr.co/edit/qNSlM0?p=preview
const el = document.querySelector('my-element');
el.attachShadow({mode: 'open'}).innerHTML = `
<!-- SEE THIS 'red' is not red -->
<p class="red">This is defined in Shadow DOM. I want this red with class="red"</p>
<slot></slot>
`;
.red {
padding: 10px;
background: red;
font-size: 25px;
text-transform: uppercase;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<!-- web components polyfills -->
<script src="//unpkg.com/#webcomponents/custom-elements"></script>
<script src="//unpkg.com/#webcomponents/webcomponentsjs"></script>
<script src="//unpkg.com/#webcomponents/shadydom"></script>
<script src="//unpkg.com/#webcomponents/shadycss#1.0.6/apply-shim.min.js"></script>
</head>
<body>
<div>
<p class="red">I'm outside the element (big/white)</p>
<my-element>
<p class="red">Light DOM content is also affected.</p>
</my-element>
<p class="red">I'm outside the element (big/white)</p>
</div>
</body>
</html>
As supersharp pointed out it's very simple but not obvious from the examples you can find on the internet. Take this base class as an example. Alternatively, you could make two different ones (e.g. Component and ShadowComponent). There is also the option to use adoptedStyleSheets or the ::part selector.
class HtmlComponent extends HTMLElement {
static ModeShadowRoot = 0;
static ModeRoot = 1;
static styleSheets = [];
static mode = HtmlComponent.ModeShadowRoot;
#root = null;
constructor() {
super();
if (this.constructor.mode === HtmlComponent.ModeShadowRoot) {
const shadowRoot = this.attachShadow({ mode: 'closed' });
shadowRoot.adoptedStyleSheets = this.constructor.styleSheets;
this.#root = shadowRoot;
} else {
this.#root = this;
}
}
get root() {
return this.#root;
}
init() {
this.root.innerHTML = this.render();
}
render() {
return '';
}
}
class Test extends HtmlComponent {
static mode = HtmlComponent.ModeRoot;
constructor() {
super();
super.init();
}
render() {
return `
<div>
<x-nested-component></x-nested-component>
</div>
`;
}
}
One of the features of Shadow DOM is to isolate CSS styles.
So if you want your Custom Elements to inherit styles from the main page, don't use Shadow DOM. It's not mandatory.
I have a custom element (without shadow DOM) that I'd like to be able to use anywhere, even inside another custom element that might use shadow DOM. However, I'm not sure how to get the styles working in both places.
For example, lets say I create a simple fancy-button element:
class fancyButton extends HTMLElement {
constructor() {
super();
this.innerHTML = `
<style>
fancy-button button {
padding: 10px 15px;
background: rgb(62,118,194);
color: white;
border: none;
border-radius: 4px
}
</style>
<button>Click Me</button>`;
}
}
customElements.define('fancy-button', fancyButton);
<fancy-button></fancy-button>
Inside a shadow DOM element, the inserted style tag will allow the fancy-button styles to work. However, if this component gets used outside of a shadow DOM element, the style tag will be duplicated every time the element is used.
If instead I add the style tag as part of the html import file, then the styles only work outside of the shadow DOM but at least they are only declared once.
<!-- fancy-button.html -->
<style>
fancy-button button {
padding: 10px 15px;
background: rgb(62,118,194);
color: white;
border: none;
border-radius: 4px
}
</style>
<script>
class fancyButton extends HTMLElement {
constructor() {
super();
this.innerHTML = `<button>Click Me</button>`;
}
}
customElements.define('fancy-button', fancyButton);
</script>
What's the best way to add custom element styles that handles both being used inside and outside the shadow DOM?
So I was able to find a solution thanks to Supersharp suggestions about checking if we're in the shadow DOM.
First you add the styles as part of the import file so that the styles apply outside of the shadow DOM by default. Then when element is added to the DOM, we check getRootNode() to see if it's been added to a ShadowRoot node. If it has, and the styles haven't already been injected into the root, then we can inject the styles manually.
var div = document.createElement('div');
var shadow = div.attachShadow({mode: 'open'});
shadow.innerHTML = '<fancy-button></fancy-button>';
document.body.appendChild(div);
<style data-fs-dialog>
fancy-button button {
padding: 10px 15px;
background: rgb(62,118,194);
color: white;
border: none;
border-radius: 4px
}
</style>
<script>
class fancyButton extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `<button>Click Me</button>`;
var root = this.getRootNode();
// In polyfilled browsers there is no shadow DOM so global styles still style
// the "fake" shadow DOM. We need to test for truly native support so we know
// when to inject styles into the shadow dom. The best way I've found to do that
// is to test the toString output of a shadowroot since `instanceof ShadowRoot`
// returns true when it's just a document-fragment in polyfilled browsers
if (root.toString() === '[object ShadowRoot]' && !root.querySelector('style[data-fs-dialog]')) {
var styles = document.querySelector('style[data-fs-dialog]').cloneNode(true);
root.appendChild(styles);
}
}
}
customElements.define('fancy-button', fancyButton);
</script>
<fancy-button></fancy-button>
When all browsers support <link rel=stylesheet> in the shadow DOM, then the inline script can turn into an external stylesheet as robdodson suggested, and the code is a bit cleaner.
You'll probably want to put the styles in a separate CSS file that you vend along with your element's JS. But as you've pointed out, if you put the element inside the Shadow DOM of another element then the styles won't work in that scope. For this reason it's usually best to just create a shadow root and pop your styles in there. Any reason why you wouldn't want to do that?