Adding classes to slotted elements (?) - web-component

I've run into an awkward scenario while working on a lit-element component library.
The component I'm currently working on is an "alert". Within it's render template it has two named slots: "title" and "text".
<div class="alert-item-content">
<i class="alert-item-icon"></i>
<slot name="title"></slot>
<slot name="text"></slot>
</div>
The elements assigned to those slots will potentially be any valid text tag (h1, h2, h3, etc.). I need to target the slots' assigned element to A) remove/reset global styles that affect them and B) add specific styles that are fundamental to UI requirements.
So, in other words, a person might provide an 'h5' because it is semantically correct but, as an alert title, it should always appear the same.
Example:
<mult-alert>
<mult-alert-item>
<h5 slot="title">Notice Me</h5>
<p slot="text">
This is something you should probably
pay attention to
</p>
</mult-alert-item>
</mult-alert>
The only solution I could come up with so far is to query the assigned elements in shadowRoot and add a class to them. It does work but feels gross. This is the relevant code from the component for that:
addClassesToSlottedNodes() {
[{
slotName: 'title',
className: 'alert-item-title'
}, {
slotName: 'text',
className: 'alert-item-text'
}].forEach(n => {
const slotNodes = this.shadowRoot
.querySelector(`slot[name="${n.slotName}"]`)
.assignedNodes();
if (slotNodes && slotNodes.length) {
slotNodes[0].classList.add(n.className);
}
})
}
Is there a better way to target these slotted elements without adding classes this way? Or, is there a cleaner way to add the class in the component template?

Inside the Custom Element template for the Shadow DOM, you could use the ::slotted() CSS function to set the CSS style of the slotted elements:
<style>
slot[name=title]::slotted(*) {
font-size: 20pt ;
font-weight: bold ;
color: red ;
}
slot[name=text]::slotted(*) {
font-family: Arial ;
}
</style>
<div class="alert-item-content">
<i class="alert-item-icon"></i>
<slot name="title"></slot>
<slot name="text"></slot>
</div>

Related

How to use nested css class (cascade) with React CSS Modules

I did see a lot of similar questions on SO but at end, it seems that my issue is a bit different..
I have a react project where (for some reason) I want two types of CSS loading:
Some global (imported from main.tsx as import 'assets/global.css';)
Some scoped (imported from a component as import style from './style.module.css';)
The global ones works as expected, it's the module one that are weird.. If I want to style a simple <a></a> within a div, this following works:
copyrights
.copyrights a { // as Link is rendered as a `<a></a>`
text-decoration: none;
}
/*...*/
import style from './style.module.css';
export const CopyrightsComponent = () => {
return (
<Container className={style.copyrights}>
<Row className={`justify-content-center`}>
<p>
{COPYRIGHTS_TEXT}
<Link to={TERMS_OF_USE_ROUTE_ROUTE}>{COPYRIGHTS_TERMS_OF_USE_HYPERLINK}</Link>
<Link to={TERMS_OF_USE_ROUTE_ROUTE}>{COPYRIGHTS_PRIVACY_POLICIES_HYPERLINK}</Link>
</p>
</Row>
</Container>
);
};
export default CopyrightsComponent;
HOWEVER! When trying to nest CSS in order to select the right child (like a specific img within a certain div), it doesn't work.. I don't understand why
foo
.foo .bar1 a {
text-decoration: none;
}
.foo .bar2 a {
color: red;
}
/*...*/
import style from './style.module.css';
export const FooComponent = () => {
return (
<div className{style.foo}>
<div className{style.bar1}>
<a>bar 1</a>
</div>
<div className{style.bar2}>
<a>bar 2</a>
</div>
</div>
);
};
export default FooComponent;
Thanks for any help...
This doesen't work since your .bar class is scoped to the element.
It should work, if you reduce the specificity of the selector to either:
.foo div a
or,
.bar a
When you utilize CSS modules, and you link your class directly to an element - the class name becomes unique, since obfuscation is appended to the class name, as per the CSS modules spec (It is scoped to the element). You can see this by inspecting a DOM element that you have linked to a class with this technique; It looks something like this: component_name-module--class_name--eq-vo.
Because of this, when you try to chain custom selectors like you did originally, the middle part of the selector (.bar) doesen't exist in its original simplicity because of this obfuscation.

How to load different .css files on different components of a react application?

I have two .jsx files that represent their respective components.
The first one is, Blog.jsx
import React from "react";
import '../assets/css/blog.css';
export const Blog = () => (
<div>
<h1>Hello Blog</h1>
</div>
)
With its respective CSS file, blog.css
div { background: #ff481f; }
and the second one is, Landing.jsx
import React from "react";
import '../assets/css/landing.css'
export const Landing = () => (
<div>
<h1>Hello Landing</h1>
</div>
)
With its respective CSS file, landing.css
div { background: #86ff2b; }
I have added routing and called instances of those two components on the App.js file which is the default file of a React application. After running the application, when navigated to components, I faced a problem that the background color is the same for both of the components (It loads landing.css only and never changes).
How can I fix that problem that each component only uses its respective .css file and loads it?
By default webpack and other build tools will compile all CSS files into one, even if css was imported in separate JSX files. So you can't use different CSS files and expect you don't affect on another part of page.
You have some options:
Use BEM for naming class names.
Use cssModules. In this case elements will have their own css class name and styles will not affect any other elements except if you use :global selector.
css-module
Using html tags as css selectors is a bad practice (because there is the behaviour you describe).
You should use only css classes or inline styles (using id's is another bad practise, id's have high priority).
div {
width: 20px;
height: 20px;
}
#id {
background: red;
}
.class {
background: green;
}
<div id="id" class="class"></div>
In case using css classes there is the same problem (when you have two similar classes). And this case is decided using css modules (set of rules of building) or you can use css-in-js (external library) which has its pros and cons. Also you can use BEM (methodology) and if your application is not big you can avoid this trouble.
css modules will add to your classes random hash and instead .my-awesome-class-name you will get .my-awesome-class-name-dew3sadf32s.
So, if you have two classes .class in the first file and .class in the second file in the end you will get two classes .class-dew23s2 and .class-dg22sf and you styles will resolve as expected.
css-in-js is a similar way except you should write your styles using javascript with some profits like including only those styles that are needed at the moment (what you are looking for right now) and several others.
You can code using pure css / scss / postcss / etc but many companies already choose between css modules and css-in-js.
BEM is just naming conventions for your class names.
And lastly - if you use inline-styles using react you should remember:
{} is constructor of object and {} returns new object on every call, it's mean if you write something like:
class MyAwesomeComponent extends Component {
render() {
return <div style={{color: "red"}}></div>;
}
}
or
class MyAwesomeComponent extends Component {
render() {
const divStyles = {color: "red"};
return <div style={divStyles}></div>;
}
}
div will re-render every time your render will call because div takes new prop.
Instead, you should define your styles (for example) in outside of your class:
const divStyles = {color: "red"};
class MyAwesomeComponent extends Component {
render() {
return <div style={divStyles}></div>;
}
}
Don't define your css using HTML tags because it will affect your entire application.
use className,id or inline styling.
like- App.css
.myApp{ color: red;}
#myApp2{ color: blue;}
App.js
import './App.css'
<div className="myApp">color changed by className</div>
<div id="myApp2">color changed by id</div>
<div style={{color:'red'}}>color changed by inline object styling</div> // inline styling
This is not the best solution if you are looking forward to improve yours css imports/loads.
However could be the best solution if you dont want to go in deep in css, resolve the problem fast and keep working with HTML tag.
You can add a div for each component, define an Id for the div and wrap the component. Afterwards in the component's css fies you are going to define all the styles starting with the #id so all the css classe or HTML tag will affect just the corresponding component.
//App render in root
ReactDOM.render(
<App />,
document.getElementById('root')
);
//App
function App(props){
return [
<Blog />, <OtherComponent />
]
}
//Blog component
function Blog(props){
return <div id="blog">
<h1>I am Blog</h1>
</div>
}
//Other component
function OtherComponent(props){
return <div id="otherComponent">
<h1>Im am other component</h1>
</div>
}
/* blog.css*/
#blog h1{
color:yellow;
}
/* otherComponent.css*/
#otherComponent h1{
color:green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Styling from separate CSS files not applied with new components

I have been trying to build a small project with React for the past few days and all went great until today. For some reason, no CSS is applied to new components! All the CSS that worked before is still up and running but if I'd add something like a div in between an already existing div, the new div will not pick up any CSS!
Example:
<div className="DivStyle"> // Styling applied!
<div className="DivStyle"> </div> // Styling completely ignored!
<div>
It is probably worth mentioning that I am still able to style the components inline.
Also, looking at the sources in Chrome, the styles are uploaded!
Here is my concrete example:
import '../styles/drawers.css';
class BottomFilterDrawer extends React.Component<IBottomFilterDrawerProps, IBottomFilterDrawerState> {
...
public render() {
return(
<Drawer
open={this.state.isOpen}
anchor="bottom"
// tslint:disable-next-line jsx-no-lambda
onClose={() => this.toggleDrawer(false)}>
<div className="BottomDrawerContainer" style={{margin: "10px"}}> // Styling for "BottomDrawerContainer" class not applied!
...
</div>
</Drawer>
);
}
}
The CSS file:
#BottomDrawerContainer {
margin: 10px;
}
I am certain that the import path is correct, Typescript wouldn't even let me run it if it weren't.
You are applying css for class not for id. So your css must be like below.
.BottomDrawerContainer {
margin: 10px;
}
if you want to apply for inner div.
.DivStyle .DivStyle {
//style for inner div
}
do not use again same id, React create error and if show any error first you solve error otherwise browser not show anything

how to protect embedded div style not to be overridden by website style

I have div with its own style. I embedded this div on other website.
<div id="scoped-div">
<style>
label {
color: green;
}
</style>
<label> Scoped div </label>
</div>
But I face problem, my div style is overridden by website style. I don't want to use iframe. Except for the use of iframe is there any other way to protect my div style by external style changes?
Your request is exactly what Shadow DOM makes possible:
attach a Shadow DOM to the element you want to protect (here:
#scope-div),
put the HTML code you want to protect in the Shadow DOM,
clone it from a <template> element to get it easy (optional).
That's it!
var div = document.querySelector( "#scoped-div" )
var template = document.querySelector( "template" )
var sh
if ( 'attachShadow' in div )
sh = div.attachShadow( { mode: "closed" } ) //Shadow DOM v1
else
sh = div.createShadowRoot() //Shadow DOM v0 fallback
sh.appendChild( template.content.cloneNode( true ) )
<template>
<style>
label {
color: green;
}
</style>
<label> Scoped div </label>
</template>
<div id="scoped-div">
</div>
There is no way to fully protect your styles. But you can try the following:
Try to specify your elements selectors as specific as possible (e.g. with attributes and IDs)
Use inline styles
Use !important (but be careful with a broad use of importants)

How do I conditionally apply CSS styles in AngularJS?

Q1. Suppose I want to alter the look of each "item" that a user marks for deletion before the main "delete" button is pressed. (This immediate visual feedback should eliminate the need for the proverbial "are you sure?" dialog box.) The user will check checkboxes to indicate which items should be deleted. If a checkbox is unchecked, that item should revert back to its normal look.
What's the best way to apply or remove the CSS styling?
Q2. Suppose I want to allow each user to personalize how my site is presented. E.g., select from a fixed set of font sizes, allow user-definable foreground and background colors, etc.
What's the best way to apply the CSS styling the user selects/inputs?
Angular provides a number of built-in directives for manipulating CSS styling conditionally/dynamically:
ng-class - use when the set of CSS styles is static/known ahead of time
ng-style - use when you can't define a CSS class because the style values may change dynamically. Think programmable control of the style values.
ng-show and ng-hide - use if you only need to show or hide something (modifies CSS)
ng-if - new in version 1.1.5, use instead of the more verbose ng-switch if you only need to check for a single condition (modifies DOM)
ng-switch - use instead of using several mutually exclusive ng-shows (modifies DOM)
ng-disabled and ng-readonly - use to restrict form element behavior
ng-animate - new in version 1.1.4, use to add CSS3 transitions/animations
The normal "Angular way" involves tying a model/scope property to a UI element that will accept user input/manipulation (i.e., use ng-model), and then associating that model property to one of the built-in directives mentioned above.
When the user changes the UI, Angular will automatically update the associated elements on the page.
Q1 sounds like a good case for ng-class -- the CSS styling can be captured in a class.
ng-class accepts an "expression" that must evaluate to one of the following:
a string of space-delimited class names
an array of class names
a map/object of class names to boolean values
Assuming your items are displayed using ng-repeat over some array model, and that when the checkbox for an item is checked you want to apply the pending-delete class:
<div ng-repeat="item in items" ng-class="{'pending-delete': item.checked}">
... HTML to display the item ...
<input type="checkbox" ng-model="item.checked">
</div>
Above, we used ng-class expression type #3 - a map/object of class names to boolean values.
Q2 sounds like a good case for ng-style -- the CSS styling is dynamic, so we can't define a class for this.
ng-style accepts an "expression" that must evaluate to:
an map/object of CSS style names to CSS values
For a contrived example, suppose the user can type in a color name into a texbox for the background color (a jQuery color picker would be much nicer):
<div class="main-body" ng-style="{color: myColor}">
...
<input type="text" ng-model="myColor" placeholder="enter a color name">
Fiddle for both of the above.
The fiddle also contains an example of ng-show and ng-hide. If a checkbox is checked, in addition to the background-color turning pink, some text is shown. If 'red' is entered in the textbox, a div becomes hidden.
I have found problems when applying classes inside table elements when I had one class already applied to the whole table (for example, a color applied to the odd rows <myClass tbody tr:nth-child(even) td>). It seems that when you inspect the element with Developer Tools, the element.style has no style assigned. So instead of using ng-class, I have tried using ng-style, and in this case, the new CSS attribute does appear inside element.style. This code works great for me:
<tr ng-repeat="element in collection">
[...amazing code...]
<td ng-style="myvar === 0 && {'background-color': 'red'} ||
myvar === 1 && {'background-color': 'green'} ||
myvar === 2 && {'background-color': 'yellow'}">{{ myvar }}</td>
[...more amazing code...]
</tr>
Myvar is what I am evaluating, and in each case I apply a style to each <td> depending on myvar value, that overwrites the current style applied by the CSS class for the whole table.
UPDATE
If you want to apply a class to the table for example, when visiting a page or in other cases, you can use this structure:
<li ng-class="{ active: isActive('/route_a') || isActive('/route_b')}">
Basically, what we need to activate a ng-class is the class to apply and a true or false statement. True applies the class and false doesn't. So here we have two checks of the route of the page and an OR between them, so if we are in /route_a OR we are in route_b, the active class will be applied.
This works just having a logic function on the right that returns true or false.
So in the first example, ng-style is conditioned by three statements. If all of them are false, no style is applied, but following our logic, at least one is going to be applied, so, the logic expression will check which variable comparison is true and because a non empty array is always true, that will left an array as return and with only one true, considering we are using OR for the whole response, the style remaining will be applied.
By the way, I forgot to give you the function isActive():
$rootScope.isActive = function(viewLocation) {
return viewLocation === $location.path();
};
NEW UPDATE
Here you have something I find really useful. When you need to apply a class depending on the value of a variable, for example, an icon depending on the contents of the div, you can use the following code (very useful in ng-repeat):
<i class="fa" ng-class="{ 'fa-github' : type === 0,
'fa-linkedin' : type === 1,
'fa-skype' : type === 2,
'fa-google' : type === 3 }"></i>
Icons from Font Awesome
This works well when ng-class can't be used (for example when styling SVG):
ng-attr-class="{{someBoolean && 'class-when-true' || 'class-when-false' }}"
(I think you need to be on latest unstable Angular to use ng-attr-, I'm currently on 1.1.4)
I have published an article on working with AngularJS+SVG. It talks about this issue and numerous others. http://www.codeproject.com/Articles/709340/Implementing-a-Flowchart-with-SVG-and-AngularJS
span class="circle circle-{{selectcss(document.Extension)}}">
and code
$scope.selectcss = function (data) {
if (data == '.pdf')
return 'circle circle-pdf';
else
return 'circle circle-small';
};
css
.circle-pdf {
width: 24px;
height: 24px;
font-size: 16px;
font-weight: 700;
padding-top: 3px;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
background-image: url(images/pdf_icon32.png);
}
This solution did the trick for me
<a ng-style="{true: {paddingLeft: '25px'}, false: {}}[deleteTriggered]">...</a>
You can use ternary expression. There are two ways to do this:
<div ng-style="myVariable > 100 ? {'color': 'red'} : {'color': 'blue'}"></div>
or...
<div ng-style="{'color': (myVariable > 100) ? 'red' : 'blue' }"></div>
Another option when you need a simple css style of one or two properties:
View:
<tr ng-repeat="element in collection">
[...amazing code...]
<td ng-style="{'background-color': getTrColor(element.myvar)}">
{{ element.myvar }}
</td>
[...more amazing code...]
</tr>
Controller:
$scope.getTrColor = function (colorIndex) {
switch(colorIndex){
case 0: return 'red';
case 1: return 'green';
default: return 'yellow';
}
};
See the following example
<!DOCTYPE html>
<html ng-app>
<head>
<title>Demo Changing CSS Classes Conditionally with Angular</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="res/js/controllers.js"></script>
<style>
.checkboxList {
border:1px solid #000;
background-color:#fff;
color:#000;
width:300px;
height: 100px;
overflow-y: scroll;
}
.uncheckedClass {
background-color:#eeeeee;
color:black;
}
.checkedClass {
background-color:#3ab44a;
color:white;
}
</style>
</head>
<body ng-controller="TeamListCtrl">
<b>Teams</b>
<div id="teamCheckboxList" class="checkboxList">
<div class="uncheckedClass" ng-repeat="team in teams" ng-class="{'checkedClass': team.isChecked, 'uncheckedClass': !team.isChecked}">
<label>
<input type="checkbox" ng-model="team.isChecked" />
<span>{{team.name}}</span>
</label>
</div>
</div>
</body>
</html>
As of AngularJS v1.2.0rc, ng-class and even ng-attr-class fail with SVG elements (They did work earlier, even with normal binding inside the class attribute)
Specifically, none of these work now:
ng-class="current==this_element?'active':' ' "
ng-attr-class="{{current==this_element?'active':' '}}"
class="class1 class2 .... {{current==this_element?'active':''}}"
As a workaround, I've to use
ng-attr-otherAttr="{{current==this_element?'active':''}}"
and then style using
[otherAttr='active'] {
... styles ...
}
One more (in the future) way to conditionally apply style is by conditionally creating scoped style
<style scoped type="text/css" ng-if="...">
</style>
But nowadays only FireFox supports scoped styles.
There is one more option that I recently discovered that some people may find useful because it allows you to change a CSS rule within a style element - thus avoiding the need for repeated use of an angular directive such as ng-style, ng-class, ng-show, ng-hide, ng-animate, and others.
This option makes use of a service with service variables which are set by a controller and watched by an attribute-directive I call "custom-style". This strategy could be used in many different ways, and I attempted to provide some general guidance with this fiddle.
var app = angular.module('myApp', ['ui.bootstrap']);
app.service('MainService', function(){
var vm = this;
});
app.controller('MainCtrl', function(MainService){
var vm = this;
vm.ms = MainService;
});
app.directive('customStyle', function(MainService){
return {
restrict : 'A',
link : function(scope, element, attr){
var style = angular.element('<style></style>');
element.append(style);
scope.$watch(function(){ return MainService.theme; },
function(){
var css = '';
angular.forEach(MainService.theme, function(selector, key){
angular.forEach(MainService.theme[key], function(val, k){
css += key + ' { '+k+' : '+val+'} ';
});
});
style.html(css);
}, true);
}
};
});
well i would suggest you to check condition in your controller with a function returning true or false .
<div class="week-wrap" ng-class="{today: getTodayForHighLight(todayDate, day.date)}">{{day.date}}</div>
and in your controller check the condition
$scope.getTodayForHighLight = function(today, date){
return (today == date);
}
One thing to watch is - if the CSS style has dashes - you must remove them. So if you want to set background-color, the correct way is:
ng-style="{backgroundColor:myColor}"
Here's how i conditionally applied gray text style on a disabled button
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
styleUrls: [ './app.component.css' ],
template: `
<button
(click)='buttonClick1()'
[disabled] = "btnDisabled"
[ngStyle]="{'color': (btnDisabled)? 'gray': 'black'}">
{{btnText}}
</button>`
})
export class AppComponent {
name = 'Angular';
btnText = 'Click me';
btnDisabled = false;
buttonClick1() {
this.btnDisabled = true;
this.btnText = 'you clicked me';
setTimeout(() => {
this.btnText = 'click me again';
this.btnDisabled = false
}, 5000);
}
}
Here's a working example:
https://stackblitz.com/edit/example-conditional-disable-button?file=src%2Fapp%2Fapp.component.html

Resources