How to style a react Link component? - css

When I try to style the Link component, it does not work:
Link {
padding: 0 30px;
}
But it does work when I use inline styling
<Link style={{padding: '0 30px'}} to="/invoices">Invoices</Link>
I use import './App.css' and styling normal elements like div does work.

Link is a react component, which by default isn't directly read/defined in native css styling.
Saying that means only one thing, Links are just anchor tags, and thus you can style/modify them in the css styling sheet using a.
To style all links available on the page (general styling) just add a { general styles..} on top of your sheet.
And then to style each one on their own, make sure its wrapped in a div with a className, and in your styles do it this way:
.divClassNameYouChose a { custom styles... }

Provide className as a prop to Link
.link-styles {
padding: 0 30px;
}
<Link className={"link-styles"} to="/invoices">
Invoices
</Link>

There are many ways you can do this:
Using id in the component
<Link id="link_Styles" to="/invoices">
Invoices
</Link>
And css is like
#link_Styles {
padding: 0 30px;
}
Using bootstrap inbuilt classes like
<Link className="px-4" to="/invoices"> //px-4 means 24px (1.5rem)
Invoices
</Link>
Using tailwindCSS classes
<Link className="px-7" to="/invoices"> //px-7 means 28px (1.75rem)
Invoices
</Link>
But for approach 2 & 3, you have to embed their CDN links respectively.
Using className like done by #Amila in first answer

Related

How to override css of other components from App

I need to make fonts for some of my components bigger when my app is in full screen mode. In my App.jsx I have variable that triggers me adding "fullscreen" class to the root DIV of the whole app. I can go brute force and override it like * { font-szie: 18px; } but thats too simple. I want to override certain classes only (like .some-class * { font-size: 18px; }). Of course React hash stands in my way so here is question: how do I apply my font size to all components in the app?
If you have hashed classes (i.e some-class-[hash]), you can use CSS selector to deal with it.
Like this:
[class^="some-class-"]
The above CSS selector will select all classes which start with "some-class-".
You can read more about CSS selectors here: https://www.w3schools.com/cssref/css_selectors.asp
You can use General Classes like Bootstrap or Tailwind, then you should use it in your public folder and use linkcss`
<head>
<link rel="stylesheet" href="..." />
</ head>
.text-sm {
font-size: 1rem !important;
}
import hashed_classes from "./file.css";
const Component = () => {
return (
<div className={`${hashed_classes.class} text-sm`} />
)
}

Ho do I add common css for stenciljs web-components

I am creating bunch of web-components, not sure how do I create common css for stenciljs web-components.
Based on documentation I can add globalStyle: 'src/global/app.css',
But it seems i can only share css variables. e.g.
:root {
--font_color: #484848;
--bg_color--light: #f9f9f9;
}
if I want to have common base css for buttons e.g.
button {
border-radius: 5px;
padding: 2px 10px;
}
Which i want to share across all the components | Not sure how to achieve that.
Thanks in advance for suggestions.
The globalStyle stylesheet gets distributed along with your app and can indeed be used to write global CSS. E. g. for the www output target, it gets generated as /build/<namespace>.css, and you can then include it into your app with a link:
<link rel="stylesheet" href="/build/my-app.css" />
However you can't use it to provide base css for elements that are inside a custom element with Shadow DOM enabled (i. e. if you have shadow: true in the component decorator).
So, as a solution you can use sass partials and modules to achieve what you're trying to do.
// src/styles/partials/_button.scss
button {
border-radius: 5px;
padding: 2px 10px;
}
// src/components/my-button/my-button.tsx
#Component({
tag: my-button,
shadow: true,
styleUrl: 'my-button.scss',
})
export class MyButton {
render() {
return <button>Click me</button>
}
}
// src/components/my-button/my-button.scss
#use '../../styles/partials/button';
The Stencil docs are a bit unclear on this issue. It took me a while to realize the globalStyle config doesn't actually do anything to apply global styles to components with shadow DOM.
If you wish to use the globalStyle globally across all components, you can try the following:
Link the globalStyle inside your index.html
Also link the globalStyle inside each of your components
The components may seem strange with a style link, but it actually works.
render() {
return (
<Host>
<link rel="stylesheet" href="/build/my-app.css" />
RENDER THIS COMPONENT WITH GLOBAL STYLES
</Host>
);
}

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();
}

Apply stylesheet to half a page with shadow-dom?

Can I apply an external stylesheet to a specific div/element with shadow-dom or via any other means? I've heard about shadow-dom and I believe it lets you constrain your styles, but that's about all I know.
Specifically, I want half the page to use bootstrap, and the other half to use MUI or something else. This is just to show how my library works nicely with different themes.
I don't want to modify the CSS in anyway to constrain it to a specific element, nor do I want to use iframes.
Yes, you can apply an external stysheet in a Shadow DOM using the #import url CSS rule.
div.attachShadow( { mode: 'open' } )
.innerHTML = `
<style>
#import url( './external-style.css' )
</style>
<!-- other elements -->`
NB: The #import rule must placed at the top of the <style> element.
You can then manipulate the Shadow DOM like a normal DOM:
div.shadowRoot.appendChild( firstSection.cloneNode( true ) )
If your content is already existing in the normal DOM, you can move it with appendChild(), duplicate it with cloneNode() as in the above example), or reveal it with the help of <slot> element:
div1.attachShadow( { mode: 'open' } )
div1.shadowRoot.innerHTML = `
<style>
:host { display: inline ; background: #cfc ; }
::slotted( span ) { color: red ; }
</style>
<slot></slot>`
<div id=div1>
<span>Hello</span> world
</div>
In the last case you'll need to use the ::slotted pseudo-element to change the style of the original DOM, so maybe you'll have to modify already existing stylesheet. The best solution depends on your use case.

Using an existing style for all buttons without specifying it in the class attribute

I am using ASP.NET MVC 3 with the Razor engine together with the newest version of the Telerik MVC controls.
In telerik.webblue.min.css there are 2 styles, namely t-button and t-state-default. It is implemented on a button element like this:
<button class="t-button t-state-default" type="submit">Apply</button>
I don't want to use a class attribute to specify which styles to use, I want to specify it in my own stylesheet that all button elements must use these 2 styles. I tried the folowing in my stylesheet but it doesn't work:
button,.t-button,.t-state-default{}
So all that I want to have in my markup is:
<button type="submit">Apply</button>
How would I do this?
UPDATE
When I view source this is what I see:
<link href="/Assets/yui_2.9.0/yui/build/reset-fonts-grids/reset-fonts-grids.css" rel="stylesheet" type="text/css">
<link href="/Assets/telerikaspnetmvc/2011.2.712/Content/telerik.common.min.css" rel="stylesheet" type="text/css">
<link href="/Assets/telerikaspnetmvc/2011.2.712/Content/telerik.webblue.min.css" rel="stylesheet" type="text/css">
<link href="/Assets/Stylesheets/hbf.css" rel="stylesheet" type="text/css">
In Firebug when I select the button it shows the top most style for this button as:
button, .t-button, .t-state-default {
}
hbf.css (line 26)
This should work.
However, you could place all the styles from
.t-button,.t-state-default {}
into a single rule labeled
button {}
EDIT
I think I see the problem, based on your update. (If I understand it correctly)
This
button, .t-button, .t-state-default {
}
appears in your hbf.css
However, it is styling nothing. button is not able to reference the other styles that way.
The .t-button, .t-state-default are still receiving styles from the telerik.webblue.min.css stylesheet.
In order to make it work, you need to add button to the telerik.webblue.min.css stylesheet.
Updated:
button,
.t-button,
.t-state-default {
border: 1px solid red;
background: #ccc;
width: 100px;
height: 25px;
}
See Demo: http://jsfiddle.net/rathoreahsan/Q2JwE/

Resources