StencilJS | Apply host styles to component - javascript

I am trying to apply the styles from the website where a stencilJS component is included ... but don't know how.
import { Component } from '#stencil/core';
#Component({
tag: 'menu-component',
styleUrl: 'menu-component.css',
shadow: true
})
export class MyComponent {
render() {
return (
<div>
<h1>Hello World</h1>
<p id="red">This is JSX!</p>
</div>
);
}
}
The component is included like this:
<link rel="stylesheet" href="css/main.css" type="text/css" />
<script src='https://unpkg.com/component#0.0.2/dist/mycomponent.js'></script>
<my-component></my-component>
In my main.css file I have something like this:
#red{
color: red;
}
I would like the style to be applied to the element from the stencil component. Is this possible?

You can use css variables for this.
Look at the following code samples:
index.html
<my-component style="--text-color:red;"></my-component>
my-component.css
#red {
color: var(--text-color, black);
}
In the component's styling you assign a CSS variable as value to the text color of the class [id="red"]. In your application, you're now able to change the color by setting the value of the variable.

Your component has a "Shadow DOM", which serves the purpose of encapsulating everything, including styles in a separate DOM, so it pretty much exists to prevent you from overriding it's styles.
Previously there were some "shadow piercing" CSS directives like /deep/ and ::shadow, but they have been deprecated and are no longer functional.
So that's pretty much how it's supposed to be.
Now for workarounds:
create a shared css file and include it in both your component and your application - or
set the style using javascript from your host application using the shadowRoot property:
var div = document.querySelector('#comp').shadowRoot.querySelector('div#red');
div.style['color'] = 'red';

You should be able to use the :host psuedo selector in your stylesheet to apply host level styles:
:host {
style: value
}
You could easily bring in #stencil.sass for your style sheets, link here: https://github.com/ionic-team/stencil-sass/blob/master/readme.md
This will give you greater functionality with your styles in stencil.
EDIT:
I misunderstood and now see you want to manipulate outside of the component. You could supply a <slot /> element in your web component and add specifically styled elements from outside of the web components DOM. Link here: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot

Related

Can I use v-if with style tag to choose a different source file? Or is there a better alternative?

I tried doing the example below, but it would not work. The reason why I am trying to do this is is that I would have to add way too many modifiers (--tuned) to make this work. So I was trying to source the css file based on a condition. Why would this not work and does anyone know if there is a better alternative that a million modifiers ?
<style v-if="!isTuningActive" lang="scss" src="./normal-version.scss"></style>
<style
v-else
lang="scss"
src="./tuned-version.scss"
></style>
Why not separate into components? Each components with his own styling.
https://vuejs.org/guide/essentials/component-basics.html
You can compile your separate scss files into a single stylesheet (main.scss for example), then just import that main.scss file into your parent component (likely, App.vue).
This will allow you to simply switch the css class on your example above instead of trying to conditionally import stylesheets.
For example, lets say your new 'main.scss' file contains the following scss.
.tuned-version {
p { color: red }
}
.untuned-version {
p { color: blue }
}
Then you'll simply need to dynamically add the class depending on your isTuningActive property.
<div :class="isTuningActive ? 'tuned-version' : 'untuned-version">
<p>My content</p>
</div>
Helpful references:
Compiling scss in Vue: https://vue-loader.vuejs.org/guide/pre-processors.html#sass
Dynamic class binding: https://vuejs.org/guide/essentials/class-and-style.html#binding-html-classes

Recreating HTML/CSS Behavior with Styled-Components

I have a fairly annoying problem using Styled-Components in a React app. The class names of the HTML elements are very important, as is the dynamism of styled-components (via props passing). So it's important that both are used.
tl;dr I want to use a classname in CSS (and not HTML) without having to create an empty component or something.
I have a class with a dynamic classname, let's stay "style-123", where "123" is dynamically changed via state and is important for parity with the current setup. (IMO, the dynamic nature of the classname is preventing a solution.) The "style-123" classname is used in other HTML elements, but it is not a component itself so should not show up as an HTML element.
I have a component ("ComponentA") that has a classname of "component-a". This component's HTML should looking like the following:
<div class="component-a">
HELLO
</div>
and its Styles (in Dev Tools) should look like this:
.style-123 .component-a {
margin-left: -2%;
}
where we now see the class ("style-123") added. This should be the final result.
The general structure of the HTML is along the lines of:
<div class="name-whatever style-123 another-classname">
<div>
<div class="component-a">
</div>
</div>
</div>
Problem
I do not seem to be able to isolate "component-a" as an HTML classname for "ComponentA" while applying CSS as .style-123 .component-a via styled-components. In order for the HTML to have an isolated classname, the component (JSX) would need to be:
<ComponentA className={'component-a'}/>
and the styled component:
const ComponentA = styled.div`
&.component-a {
color: red;
}
`
This is the only way I know how to link-up a component in JSX with its styled-component counterpart.
I cannot do:
<ComponentA className={'component-a'}/>
and the styled component:
const ComponentA = styled.div`
&.style-123 .component-a {
color: red;
}
`
as this won't apply the styling since there's a classname-mismatch.
I've tried many combinations of SASS, all the ampersands and arrows and pluses and etc., over the last few hours. I hope this is feasible via styled-components as I need to declare classnames and have classnames dynamic as well as the CSS attributes and values, all passed via props. If there is another solution that doesn't use styled-components but maintains this dynamism, I am open to that.
Thanks for the time.

How can I use style tag in JSX

So I'm in a situation where I have to use <style> tag, That is because I have multiple pages (I'm using react router DOM) so I cant apply the same style to every page as that would mess things up. Is there a way I can use <style> tag in JSX?
I have to use style tag like this:
function RandomScreen(){
return (
<div>
<style></style>
</div>
)
}
You can simply import a stylesheet into your page:
import './main.css'
Another solution is to use Emotion and the Global component:
https://emotion.sh/docs/globals
Can you use separate css file for every component and import every css file in every component you need.
import './RandomScreen.css';

Styling a {#html...} tag of a Svelte component by ising the in-component <style> tag (Unused CSS selector)

I am trying to add some styling to an HTML tag rendered inside of the {#hml...} tag of a Svelte component, but it appears that it only inherits the parent's styling (the container of the {#html...} tag). Moreover, the Unused CSS selector error pops up displaying that my styling selector for that specific HTML tag inside of the Svelte {#html...} tag merely doesn't work. Is Svelte built that way and is there a method for styling tags that are rendered inside of the Svelte {#html...} tag?
I've tried steps provided in the official Svelte tutorial, but they don't clearly show how to do it.
<style>
p{
color: red;
}
h1{
color: blue;
}
</style>
<script>
let string = "<h1>what</h1>";
</script>
<p>{#html string}</p>
<p>no</p>
I want the h1 tag to be blue, not inherit the red color from the p tag
You need to use the :global(...) modifier. If you do this...
:global(h1) { color: blue }
...it will apply to all <h1> elements on the page — both the one inside the {#html ...} tag and any that exist in other components.
To restrict it to elements inside this component, put the selector inside a local one:
p :global(h1) { color: blue }
That selector will only apply to <h1> elements inside <p> elements belonging to this component. Here's a demo
It could be a bug (but it seems difficult for Svelte to know what could be there in that string).
As (an ugly) workaround, you may choose to specify required style inlined. For example,
<script>
let string = `<h1 style="color:blue;">what</h1>`;
</script>
EDIT: And Svelte creator has answered the official way is using :global.

Duplicated styles on head and a lot of <style> elements

I'm quite happy with angular-material2 components but there are some weird behaviors that I don't understand and they don't have a proper documentation especially for teeming and customizing their components.
My project looks like:
.src
--app
--components
--login-component
login-component.html
login-component.scss
login-component.js
--login-component
home-component.html
home-component.scss
home-component.js
--and so on ...
app.component.html
app.component.scss
app.component.ts
app.module.ts
app.routing.ts
--assets
--environments
--scss
styles.scss
_material2-theme.scss
_variables-scss
_utilities.scss
_reset.scss
favicon
index.html
and so on ....
In angular-cli.json I have modified the styles to look at scss/style.scss
...
"styles": [
"scss/styles.scss"
]
...
the _material2-theme.scss looks like:
//////////////////////* THEMES */ Custom Blue Theme*/
#import '~#angular/material/theming';
#include mat-core();
$app-primary: mat-palette($mat-light-blue);
$app-accent: mat-palette($mat-light-blue, A200, A100, A400);
$app-theme: mat-light-theme($app-primary, $app-accent);
#include angular-material-theme($app-theme);
/*default palette forground/background*/
$light-foreground-palette: map-get($app-theme, foreground);
$light-background-palette: map-get($app-theme, background);
$primary: map-get($app-theme, primary);
$accent: map-get($app-theme, accent);
and inside of style.scss I am importing everything to be compiled with scss cli compiler
//////////////////////* CUSTOM */
#import "_material2-theme.scss";
#import "_reset.scss";
#import "_utilities.scss";
//////////////////////* COMPONENTS */
#import "~app/components/login/login.component.scss";
My question is after the scss is compiled we have in html head many style tags some of them duplicated and look like:
Everything seems to be compiled in one style that is added in head(tha one that has type attribute) and and after that each scss component splited in each css component with its separate style in head, and that is very weird. I am doing something wrong or is just tha way is working material2?
The behavior you're seeing is caused by ViewEncapsulation.Emulated defined for material components.
First, you don't need to add styles for components to the global styles "scss/styles.scss":
//////////////////////* COMPONENTS */
#import "~app/components/login/login.component.scss";
If you do that, besides your styles getting duplicated you will lose style encapsulation because styles added for components/login/login.component.scss will become global.
Now, to the question why there are many style elements. When you use ViewEncapsulation.Emulated, and it's a default view encapsulation mode, Angular will put each component's style into its own style tag and add attribute to elements inside a component template. In material all components use emulated encapsulation mode so for each component the style tag is added.
If you add #Component({..., encapsulation: ViewEncapsulation.Native }) for your components, you will see the style for your components will be removed.
Here is the logic that adds the style tag:
export class DomSharedStylesHost extends SharedStylesHost implements OnDestroy {
...
private _addStylesToHost(styles: Set<string>, host: Node): void {
styles.forEach((style: string) => {
const styleEl = this._doc.createElement('style'); <--- creates a style element
styleEl.textContent = style;
this._styleNodes.add(host.appendChild(styleEl));
});
}
In Material design for Angular the stylesheet is split into:
style - margins, paddings etcetera that is linked from the component
theme - mostly colors that is build using the theme scss
typography - fonts & font properties that is built using the typography scss
This causes similar selectors, but with different properties inside.
I think it is the live development server that comes with angular cli or webpack that loads css dynamically which causes duplication of style tags. I believe this is not happening in a production build.
PS. Strange of you to add _reset.scss after anything else.
I do not think it is due to having separate styles for each component.
This issue is similar to what's there in a ngularjs-material as well.
Angular-material includes some default theme css as a const variable in JavaScript.
You can read a more on this issue ,
https://stackoverflow.com/questions/33466779/how-to-get-rid-off-angular-material-extra-styles-and-css-linked-by-it-forcefull
In development, the angular cli or webpack compiles every css/scss files from each component to a style tag in the HTML page.
ie, in your case: login-component.scss, home-component.scss each will be loaded inside seperate style tags in the HTML page.
All these will be compiled into a single css file and will be linked in index.html in production conf.
So I too think its the way angular works!
Currently I see that there is only one file generated in production build ng build --prod .
There are multiple style tag in your head may be your every component is linked with some external different css Or you have written internal css at related HTML page.
If you want that there is only one style tag throughout application then in that case you have to write all style in single file.

Categories