Ionic CSS classes assignment - css

I’m styling my app. Familiar with the basic theming components, SASS etc... But one thing that stands out and it not making sense is why when I preview the source in a running app how lots of extra CSS classes are added. I’m my case I am simply trying to change the menu header background. In my app.html file I have;
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
Which translates to;
<ion-header class="header header-md">
<ion-toolbar class="toolbar toolbar-md">
<div class="toolbar-background toolbar-background-md" ng-reflect-klass="toolbar-background" ng-reflect-ng-class="toolbar-background-md"></div><div class="toolbar-content toolbar-content-md" ng-reflect-klass="toolbar-content" ng-reflect-ng-class="toolbar-content-md">
<ion-title class="title title-md"><div class="toolbar-title toolbar-title-md" ng-reflect-klass="toolbar-title" ng-reflect-ng-class="toolbar-title-md">Menu</div></ion-title>
</div></ion-toolbar>
</ion-header>
I see that there seems to be a pattern of 'ion-element' translating to 'element element-md'. But it gets a little strange for elements such as 'ion-toolbar' as this adds a div
<div class="toolbar-background toolbar-background-md" ng-reflect-klass="toolbar-background" ng-reflect-ng-class="toolbar-background-md">
I’d like to understand how this translation process works for me I'd like to create some tidy CSS as its looking a bit unwieldy to me!

ion-header is an angular directive (as is ion-toolbar). Directives can have an associated HTML template and javascript. The extra classes are added by the javascript that belongs to each directive. The code below demonstrates an angular directive where a class is added to the original element. If you inspect the results you can also see an extra div being added - that is the result of the directive's template being added to the DOM.
(function() {
var app = angular.module("soDemo", []);
app.directive("soDirective", SoDirective);
function SoDirective() {
var directive = {
restrict: 'E',
scope: {},
link: link,
template: '<div class="content">My directive contents</div>'
};
return directive;
///////////////////
function link(scope, element) {
element.addClass('sample-class');
console.log(element.html());
}
}
})();
.sample-class {
display: block;
border: 1px solid red;
}
.content {
font-style: italic;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="sample" ng-app="soDemo">
<so-directive></so-directive>
</div>

If you look at the Header Ionic API Docs, for <ion-header> you'll see there are Sass variables for 3 platforms - the abbreviation to right of colon is used to target platform specific styling rules:
iOS : ios
Material Design (default if in browser like Chrome) :md
Windows Platform : wp
Plaform specific styles confirm that here
When you do ionic serve --lab you have the option to choose platform and get a feel for the default files render.
When you want to target CSS for specific platforms, then the
nomenclature is: [<elementname>-<platform-abbreviation>]
Here's a more complete list of SASS variables for the different Ionic Components.
In your project there is a themes/variables.scss where you can use those values.
You can define your own SCSS variables like $my-padding:5px; in here and then use that in your custom component CSS, and consequently have that constant declared once and you can globally apply.
So my-component.scss has
[ion-item] {
padding: $my-padding;
}
The toolbar source folder is here.
toolbar-header.ts contains the #Directive selector 'ion-header' for <ion-header> Typescipt class name: Header
toolbar.ts contains the #Directive selector 'ion-toolbar' for <ion-toolbar> Typescript class name: Toolbar
There are scss files for the platforms..
toolbar.ios.scss
toolbar.md.scss
toolbar.wp.scss
They seem to import:
ionic.globals.scss
Furthermore you can configure things in your app-module.ts.
#ngModule({
....
imports: [
IonicModule.forRoot(MyApp, { // settings object
iconMode: 'ios', // all platforms globally use ios icons
tabsHighlight: true, // seems to work only on android
platforms: {
android: { tabsPlacement: 'top' // platform specific
}
}
})
This should give you an insight

Related

Target Angular binding with SCSS / CSS

I am migrating an application from AngularJS to Angular. I have just moved a loading-indicator component to Angular, and that has an optional modal binding to say whether its a modal loader or not.
In reality this doesnt bind to anything right now, it simply matches a CSS rule.
HTML
<loading-indicator [modal]="true"></loading-indicator>
SCSS
loading-indicator {
&[modal="true"] {
background: rgba(255,255,255,0.5);
}
}
The SCSS above targets an attribute of modal not [modal].
How do I target [modal]? I saw some answers suggesting I can escape the brackets using \\ e.g. &\\[[modal\\]] but this didn't seem to work.
You cannot. Because [modal]="true" for Angular it's an input and this will be converted to another HTML attribute.
You can use modal=true in your component and you can select this with
[modal=true] {
/*styles*/
}
But for encapsulation, you need to put this style on the root styles of the app.
It's a better solution to bind the input modal value to a [ngClass]="":
Inside the component ts loading-indicator:
#Input() modal;
On the template use:
<div [ngClass]="modal ? 'modal-background' : ''"></div>
On the styles of component:
.modal-background{
background: rgba(255,255,255,0.5);
}

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>

Dynamically switch global CSS for Angular8 app (client branding)

I want to dynamically switch Angulars global CSS files based on which client is connecting. This will be used for client-branding purposes, including fonts, colors, photos, headers, footers, button-styles, etc.
Each client has provided us with a CSS file, which we need to integrate into our app. We have hundreds of clients.
Current solution is to try and override the CSS of individual components at load. This is bad because it adds a lot of boilerplate:
Html:
<link id="theme" rel="stylesheet" href="./assets/stylesheets/{{cclientCode}}.css">
ts:
ngOnInit() {
this.service.clientCode.subscribe(clientCode => this.clientCode = clientCode);
}
My workaround isn't working because the link html is called before the {{}} has a chance to load in the value.
I'm also not motivated to fix my workaround because its just that -a workaround. Instead, I want to implement something that works globally, without any per-component boilerplate.
What I want is the ability to dynamically switch the global Angular style for each client. So something like:
"styles": [
"src/assets/stylesheets/angular_style.css",
"src/assets/stylesheets/client_style.css"
]
Where client_style.css is served differently to each client.
I've found a solution that I think is workable. It definitely has issues though, so if anyone has their own answer, please still share!
First, I added a clientCode String field to SessionDataService, a global service I use to move component-agnostic data around my app:
export class SessionDataService {
clientCode: BehaviorSubject<String>;
constructor(){
this.clientCode = new BehaviorSubject('client_default');
}
setClientCode(value: String) {
this.clientCode.next(value);
}
}
Then, inside app.component.ts, I added a BehaviorSubject listener to bring in the value of clientCode dynamically:
public clientCode: String;
constructor(private service : SessionDataService) {
this.service.clientCode.subscribe(clientCode => this.clientCode = clientCode);
}
Next, I added a wrapper around my entire app.component.html:
<div [ngClass]="clientCode">
--> ALL app components go here (including <router-outlet>)
</div>
So at this point, I've created a system that dynamically adds client-code CSS classes to my components, including all children :)
Finally, I just have to write CSS rules:
.ClientOne p {
color: red;
}
.ClientOne .btn {
background-color: red;
}
.ClientTwo.dashboard {
height: 15%;
}
I hope this helps somebody! Essentially the "trick" here is to add a ngClass that wraps the entire app, and then justify all client-specific CSS rules with their client code.

Why doesn't Font Awesome work in my Shadow DOM?

Font Awesome is not working in my shadow DOM since I have the following in it to prevent styles from leaking in and out:
:host {
all: initial; /* 1st rule so subsequent properties are reset. */
display: block;
contain: content; /* Boom. CSS containment FTW. */
}
I'm able to use other stylesheets by just inlining them within the :host property, but that doesn't work with Font Awesome since it uses relative paths in its stylesheet.
I found this post and tried it with the scoped CSS I implement, but the icons show as squares, as can be seen in my example.
I had the same issue with StencilJS.
After hours of struggle and the answer from #Intervalia I was able to fix it.
The problem is that the browser doesn't load the font files when they are only included in the shadow dom (your custom web component). This means that the fonts also has to be included in the normal html file (aka light DOM) so that the browser can detect and load them in order to make them available in the shadow dom.
In my case I didn't use Font awesome instead it was a custom font but I tried it a second time with font awesome and a clean Stenciljs project. The solution is always the same doesn't matter which custom font you need.
Step 1: Move the font into your project. I created a seperate "assets" folder inside the "src" folder to have access from all the components. In this example I downloaded font awesome for web environment https://fontawesome.com/download. (I wouldn't recommend "npm install" since you have to use it in the index.html too)
Step 2: Prepare your web component (in this case my-component.tsx). You can import multiple css files using the styleUrls property. Just import the fontawesome css from your assets directory.
import { Component, Prop, h } from '#stencil/core';
#Component({
tag: 'my-component',
styleUrls: [
'my-component.css',
'../../assets/fontawesome/css/all.css'
],
shadow: true
})
export class MyComponent {
#Prop() first: string;
render() {
return <div> <i class="fas fa-question-circle"></i> </div>;
}
}
Step 3 prepare the file where you want to use the component (in this case index.html). The important line is the "link" tag. This includes the "font awesome css" again and force the Browser to really download the fonts.
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<title>Stencil Component Starter</title>
<link rel="stylesheet" type="text/css" href="./assets/fontawesome/css/all.css">
</head>
<body>
<my-component first="Stencil" last="'Don't call me a framework' JS"></my-component>
</body>
</html>
I know it feels wrong and looks weird but it is not enough to include font awesome only in the index html or in the web component. It must really be included in both files. That doesn't mean the Browser will load it multiple times - it will only be loaded once.
That means that you can't deliver the font with the web component - as far as i know. This isn't a stenciljs bug this is a general problem of the browsers. Please let me know if you have better solutions.
Just for fun here is a screenshot that shows that the browser doesn't load the fonts when it is only included in one file. http://prntscr.com/p2f9tc
Update 05.10.2019:
If you want to use your font inside your web-component the explanation above is correct and still necessary. But you can also use the slot tag inside the web-component. Than you automatically bypass the font from outside (the html) into the web-component. But notice it only works for the stuff you write between the tags of your web component.
That means you can use <my-component> <i class="your-font"/> </my-component>. In this case you don't have to import the font into the web components.
One thing I have noticed is that if the page does not load the CSS file then the shadowDOM won't load it either.
I really think that the only problem us that if the font is not defined on the page that it will not work in the component since the rest of the CSS seems to properly apply to the shadowDOM elements.
This example shows just the shadowDOM trying to load the CSS and it does not work:
let template = `
<style>
:host {
display: block;
}
</style>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<header>
<h1>DreamLine</h1>
<nav>
<ul>
<li>Tour</li>
<li>Blog</li>
<li>Contact</li>
<li>Error</li>
<li><i class="fa fa-search"></i> Search</li>
</ul>
</nav>
</header>
`;
class MyEl extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'}).innerHTML = template;
}
}
customElements.define("blog-header", MyEl);
<i class="fa fa-battery-full" style="font-size: 45px;"></i>
<hr/>
<blog-header></blog-header>
<hr/>
And this example shows both the page and the shadowDOM loading it and it works:
let template = `
<style>
:host {
display: block;
}
</style>
<header>
<h1>DreamLine</h1>
<nav>
<ul>
<li>Tour</li>
<li>Blog</li>
<li>Contact</li>
<li>Error</li>
<li><i class="fa fa-search"></i> Search</li>
</ul>
</nav>
</header>
`;
class MyEl extends HTMLElement {
connectedCallback() {
const styles = document.querySelector('link[href*="fontawesome"]');
this.attachShadow({mode: 'open'}).innerHTML = template;
if (styles) {
this.shadowRoot.appendChild(styles.cloneNode());
}
}
}
customElements.define("blog-header", MyEl);
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<i class="fa fa-battery-full" style="font-size: 45px;"></i>
<hr/>
<blog-header></blog-header>
<hr/>
The code I like to use looks for the <link> tag I want in the body and then uses a clone of that tag inside the shadowDOM. This way my component is not out of sync. Yes, this can cause problems if the component was not expecting a change in the CSS but I find it works well for my projects.
If you don't need shadow: true then you can load the all.min.css directly via the index.html or the main application. Even loading the all.min.js file works.
If you need it within the shadow dom, then you need to load the all.min.css in index.html and also load it within shadow root using something like this.
`
componentDidLoad(): void {
this.hostElement.shadowRoot
.getElementById("some_Id")
.insertAdjacentHTML(
"afterbegin",
`<link rel="stylesheet" href="${getAssetPath(
"/fontAssets/fontawesome-free/css/all.min.css"
)}" />`
);
}
`
I wanted to share what I did for loading in Font Awesome icons to my stencil components (shadow enabled)...
After several hours into researching this topic, I think I've discovered a solution that will be the most efficient for my components to be bundled in a agnostic way and free of any additional style sheet includes in the HTML header.
My solution was to use the stencil-inline-svg module and then import the svg file directly from the Font Awesome module like this:
// the reference to the svg can be anything (searchIcon name).
// just make sure to import the correct svg from fontawesome node modules.
import searchIcon from 'fontawesome/svgs/regular/search.svg';
#Component({
tag: 'search-icon',
styleUrl: 'search-icon.scss',
shadow: true,
})
export class SearchIconComponent {
render(){
return (
{/* Not ideal to use innerHTML but this renders the full SVG markup */}
<span class="search-btn" innerHTML={searchIcon}></span>
)
}
}
Now, I can set css rules to modify the color and size of my icon like this
.search-btn {
width: 40px; // Set SVG size at the parent.
svg path {
fill: #fff; // Update svg path color.
}
}
Obviously this requires a little bit of Font Awesome icon knowledge so you know which icons to import.
Shadow Doms's style is scoped. And it does not interfere with outer style
FWIW I created a helper method to create a link for font-awesome at the parent page level. Not sure if this is in violation of any custom-elements/Web Components standards but I'll go ahead and post here in hopes that I'll be corrected :) It works for my use case w/ internal web applications for now though.
export const addFontAwesomeStyles = () => {
injectStylesheet("https://use.fontawesome.com/releases/v5.13.0/css/all.css");
injectStylesheet("https://use.fontawesome.com/releases/v5.13.0/css/v4-shims.css");
}
export const injectStylesheet = (href: string) => {
const links = document.head.querySelectorAll("link");
// Already been injected
for(let l in links)
if(links[l].href == href) return;
const link = document.createElement('link');
link.rel = "stylesheet";
link.href = href;
document.head.appendChild(link)
}
Then in your StencilJS component's construtor you can use it like so:
//...
constructor() {
addFontAwesomeStyles();
}

Applying CSS stylesheet only to active component

I'm working on a ReactJS app that has a header at the top, a menu on the left, and the "frame" in the middle is where routes and their corresponding components are loaded. I want to be able to apply a CSS stylesheet to specific components only when they are loaded. I also don't want them applied all the time or to the top header or left menu.
My expectation was that adding import 'custom.css'; to a specific component would only apply the stylesheet's styles to that component and it's children when the route is active. Instead, it applies it to the entire page even when the route/component are not loaded.
I understand that an alternative approach is styled components, but, for my use-case, a design company is supplying a stylesheet (which should remain unchanged) that we need to consume only for the sub-module I'm working on and I don't want its styles to affect the rest of the app.
How can I have a stylesheet only applied to my active route/component?
Use simple CSS technique. Suppose you have two components with different css files (say about.css and contact.css). Now consider your both CSS file have one common class with different style properties, like:
about.css
.container{
max-width: 400px;
}
contact.css
.container{
max-width: 500px;
}
Yes in ReactJS both the CSS files will load at the same time and will override any one of the style. so to solve this problem add class to differentiate this styles, like:
about.css
.about-component.container{
max-width: 400px;
}
contact.css
.contact-component.container{
max-width: 500px;
}
If you want apply only when the component is mounted, you can use the lifecycle.
The follow example is based in the idea you are using sass, React, sass-node and have the loaders into webpack.
<pre>
import React from 'react';
import './styles.scss';
class MyComponent {
constructor(props) {
super(props);
this.state = { className: '' }
}
componentDidMount() {
this.setState({
className: 'myOwnClass'
});
}
render(){
return (
<div className={this.state.className}>This is a example</div>
);
}
}
export default myComponent;
</pre>
To be able to only call that specific CSS when you need it you can use CSS Modules. You may need to update your version of react.
When saving your CSS file save it with a ".module.css" eg. "styles.module.css". The CSS in these files can only be used and accessed by hte components where are they are imported. As stated in a tutorial from W3Schools.
Let's say this is your CSS code in styles.module.css:
.container {
color: white;
}
.cont-child {
background-color: red;
}
Then in your JS file you can import the CSS file like this if the JS and CSS files are in the same directory. Make sure you point to the correct path.
import styles from './styles.module.css'
Then in your HTML section you can use it like this:
class Home extends React.Component {
render() {
return(
<main className={ styles.container } >
<div className={ styles["cont-child"]} >
Some div text about something...
</div>
</main>
);
}
}
I currently use both ways to access the selectors, since the styles variable acts like an object. I placed both of them here because the second option is capable of fetching selectors named like "btn-active". Which comes in handy in some situations. Camelcasing is considered cleaner though.
Please note: I originally posted this answer as a reply to a similar question here React CSS - how to apply CSS to specific pages only
I want to be able to apply a CSS stylesheet to specific components
only when they are loaded.
Why not apply the styles inline via React.js?
Step 1. Create the style object for the component:
var componentOneStyle = {
color: 'white',
backgroundColor: 'red'
};
Step 2. Populate the component's style attribute with the style object:
ReactDOM.render(<div style={componentOneStyle}>This is Component One</div>, mountNode);

Resources