Vue 3 : disable shadow dom while using custom element - vuejs3

I would like to add in a non-vue application a custom element.
For that, I've created a classical SFC :
//test.ce.vue
<template>
<div class="text-primary">Test</div>
</template>
<script>
export default {
name: 'test',
};
</script>
<style>
.text-primary {
color: red;
}
</style>
And then a main script :
//app.js
import Test from 'test.ce.vue';
const testElement = defineCustomElement(Test);
customElements.define('test-element', testElement);
document.body.appendChild(document.createElement('test-element'));
Everything is running normally with the creation of a shadow dom element :
<test-component>
#shadow-root (open)
<style>
.text-primary {
color: red;
}
</style>
<div class="text-primary">Test</div>
</test-component>
I would like to avoid to redefine .text-primary class in the component as this class is already defined in the main css file. I also don't need to define specific classes for this component only, so in other terms, I would like to remove the shadow dom like a classical custom element will do.
So basically, render this :
<test-component>
<div class="text-primary">Test</div>
</test-component>
Is there's any option to define in vue that permit that ?

Older question, but in case someone still needs a solution for this...
there is currently no way to tell Vue not to use the shadow-dom. In Vue 2 there was a official package for creating web-components without shadow-root. And there is a community port for Vue 3 of that:
https://www.npmjs.com/package/vue3-webcomponent-wrapper
It was only meant to help people who have just migrated from Vue 2 to keep there application working. It was never intended to replace the official solution and should only be used until the official package can handle Vue 3.
Unfortunately that never happend.
The community port still works, but the package does not contain any source code, so it is a bit scary to use.
I came up with another solution for our project. Using defineCustomElement on a more complex vue component wich is composed by a bunch of nested components reveals another problem. The css of the child components wont be copied to shadow root. So only the css of the root component will work.
You can find the related issue and a workaround with full example here:
https://github.com/vuejs/core/issues/4662#issuecomment-1116001438
It basically grabs the css from the head and appends it to the shadow root.
You just have to extend it to also copy your main.css, like
<template>
<div id="app" ref="injectionElementRef">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import HelloWorld from './components/HelloWorld.vue';
export default defineComponent({
name: 'App',
components: {
HelloWorld
},
mounted() {
const el = this.$refs.injectionElementRef as HTMLElement
const root = el.getRootNode()
const linkTag = document.getElementById('main-css-id')
root.insertBefore(linkTag.cloneNode(), el)
}
});
</script>
The downside of this method is, there is a short flicker because the css is applied after mount. You could show an empty element till css is applied to work around that.

You are using Vue as a Tool to create Web Components, but why use a Tool over Native Technology?
Tools are not better; Tools are only faster in performing a task.
And in your case the Tool does something you do not want it to do.
Using native Web Components Technology, all you need is:
<style>
.text-primary {
color: red;
}
</style>
<test-component></test-component>
<script>
customElements.define("test-component", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<div class="text-primary">Test</div>`;
}
})
</script>

Related

Applying global styles in Next.js

I am using Next.js with Typescript. The margin of the body is default 8px and I want to get it to 0px. When I try to add an external style sheet to my index.tsx file it throws an error that you can only add external stylesheet to _app.tsx. However, even when I try to import in my _app.tsx, it doesn't change the global style of the body. I am using Emotion css for the styling part. Is there a different way to change the style of the body in the index file using global style? Here is my index.tsx code and I have tried adding the global styles using Emotion CSS as well but it doesn't work.
class Index extends React.Component {
render() {
return (
<div className='body'>
<Container>
<style jsx global>{`
.body:global() {
margin: 0px;
}
`}</style>
<NavBar />
</Container>
</div>
);
}
}
You need some global styles (<style jsx global>) to style the body element or provide css resets.
Example:
import Link from "next/link";
export default () => (
<div>
<style jsx global>{`
body {
background-color: red;
}
`}</style>
Hello, One!
<Link href="/two">
<a>Go to two</a>
</Link>
</div>
);
Code Sandbox
You can have global styles using emotion with Next.js
In your _app.tsx file, you must to
import { Global, css } from '#emotion/core'
return (
<>
<Global styles={css` /* styles */ `}/>
<Component {...pageProps} />
</>
You can see how to implement it, here
https://github.com/pabloobandodev/social-media/blob/master/pages/_app.tsx
According to the official docs:
Global CSS cannot be used in files other than your Custom <App> due to its side-effects and ordering problems.
Possible Ways to Fix It
Relocate all Global CSS imports to your pages/_app.js file.
How to do this in your case?
Well, the best way is to use a CSS base, lets take normalize.css for example.
Run yarn add normalize.css or npm i normalize.css, depending on whichever you are using.
Add import 'normalize.css'; in each of the page you want to use the base on. Official Docs.
Well this could seem redundant if you want to use the base in all of your pages. If so, you can, alternatively, create a file page/_app.tsx (any of the extension .js,.jsx,.ts,.tsx will work) and put this in it:
import 'normalize.css';
export { default } from 'next/app';
Note : If your app is running and you just added a custom App, you'll need to restart the development server. Only required if pages/_app.tsx didn't exist before.
No need to worry about other caveats mentioned in the docs as we are simply re-exporting App without any modification.
There are many CSS bases available choose any that seems best for you.
If you want to add custom global styles, then follow this:
Create a file styles/globals.css (.scss,.sass,etc. will also work if you have configured Next.js properly) and put your styles in that file.
Now add an import in pages/_app.tsx.
import '../styles/globals.css'; // change extension from `.css` to
// whatever you created above
export { default } from 'next/app';
If you have already created a module path alias for ../styles, then you might wanna change the styles import statement (probably to something like import '#styles/globals.css').
Also, if you are using less/sass/scss and want to use a base at the same time along with your custom global styles you simply need to use an import statement in your stylesheet (no need to import the base in _app.tsx if imported in the global stylesheet). An example:
// file: styles/globals.scss
#import '../node_modules/normalize.css/normalize.css';
// your styles...
body {
color: red;
}
// file: pages/_app.tsx
import '#styles/globals.scss';
export { default } from 'next/app';
Moreover, in your case it has not worked most probably because you were styling .body instead of body. It is likely that margin was present in the body, not your div.body.
This is how your _app.js, _app.tsx should look like; styles.css may have your CSS to reset the default browser properties, you can try adding other stylesheets here.
import '../styles/styles.css'
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}

How to create and style svelte 3 custom elements with nested components?

I'm trying to create custom-element (web component) in svelte 3. I can't find any way to style nested components from css. Svelte removes styles before injecting them to <style> inside of ShadowDOM.
The problem is that I want to have nested components in my root element.
For example:
RootComponent (svelte custom-element)
(imports) FooComponent
(imports) BarComponent
As stated here: svelte-custom-element
All the components imported to custom-element must have compiler option set to <svelte:options tag="component-name" />.
With this option set nested components works as expected and are injected into root's element ShadowDOM. The problem is that styles defined in nested components are not being injected.
The workaround for this problem would be to inject them into root's element <style> as global styles within ShadowDom.
(Un)fortunately svelte automatically removes all unused styles during compilation when custom elements not yet exist.
My goal is to create web component with svelte and then use it outside of svelte as native web-component.
Here is REPL
Custom elements do not really work on REPL as Conduitry wrote:
The compiler options in the REPL don't actually affect the code that >is run, just the code that is displayed. So enabling customElement >doesn't mean you are building and running a web component
So it's more like a code example than working one.
I would like to know if there is another way to create svelte custom-element with nested component and proper styling.
Is there a way to disable removing of unused css?
https://imgur.com/a/zZia566
from <div class="nested"> starts Nested component imported from Nested.svelte.
<style> element should have .nested class injected but it is removed by svelte compiler.
This is because when customElement option is on, each style in a component is injected into the shadowRoot of the custom element.
class YourComponent extends SvelteElement {
constructor(options) {
super();
this.shadowRoot.innerHTML = `<style>.foo{color:red;}</style>`;
// continues
Thus, in order to make style appear, you must use svelte component as custom element, not as svelte component.
Your App.svelte should be like below.
<script>
import Foo from './Foo.svelte'
import Bar from './Bar.svelte'
</script>
<svelte:options tag="web-component" />
<foo-component/>
<bar-component/>
However, this neither solve the problems related with custom element.
:global selector is not transformed into actual global selector.
Every nested component will produce shadowRoot, whereas mostly you will want only top-level one.
Check out some issues below from svelte repository related to custom elements.
nested component in custom element does not inherit style #2605
:global(...) not working in custom elements #2969
It seems like svelte does not fully support style cascading in custom element yet, should be handled in future.
Checked in svelte v3.12.1.
Thanks to brunoalano for sending me this: svelte-custom-element-template. It solves the styling problem with custom build script.
You just have to prevent the compiler from removing unused CSS
Let's say we have a custom element : App.svelte
App.svelte imports a normal svelte component : ./components/Message.svelte
But when you do this, any styles inside Message.svelte will disappear.
Solution
Move all content in the <style> tag of Message.svelte into the <style> tag of App.svelte
Add this to script of App.svelte
let cssKeep: string = "";
Add this to body of App.svelte
<span style="display: none;" class={cssKeep}><span class={cssKeep} /> </span>
This will prevent the compiler from removing any styles
Example
src/components/Message.svelte
<script lang="ts">
export let content: string;
</script>
<p class="red"> {content} </p>
src/App.svelte
<svelte:options tag="my-element" />
<script lang="ts">
import Message from "./components/Message.svelte";
let cssKeep: string = "";
</script>
<Message content="hello" />
<span style="display: none;" class={cssKeep}><span class={cssKeep} /> </span>
<style>
.red {
color: red;
}
</style>
vite.config.ts
import { defineConfig } from 'vite'
import { svelte } from '#sveltejs/vite-plugin-svelte'
export default defineConfig({
build: {
lib: {
entry: './src/main.ts',
name: 'MyElement'
},
},
plugins: [
svelte(
{
compilerOptions: {
css: true,
},
exclude: "./src/App.svelte",
emitCss: true,
}
),
svelte(
{
compilerOptions: {
customElement: true,
css: true,
},
exclude: "./src/components/**",
emitCss: true,
}
),
],
})
// guide: https://www.thisdot.co/blog/web-components-with-svelte
This may answer:
How to export web component in Svelte with nested Svelte components
Use Svelte components with custom elements
Styles missing when Svelte component imported into custom web component / custom element

CSS className isn't making any changes to Reactjs

I'm currently working with rails and reactjs. I'm having difficulties using css in my reactjs files. It seems like every time i try to use it, no change is being applied at all. In my App.jsx file I have this:
import React from "react";
import styles from "./styles.css";
export default class Register extends React.Component {
render() {
return (
<div className={styles.container}>
<h1> this text should appear to the right </h1>
</div>
);
}
}
And in my styles.css file I have this:
.container {
width:40%;
text-align:right;
}
For the record I am using webpack. Can anyone help me understand why the css isn't having any effect on my jsx components. I've looked all over for help but was unable to put the pieces together.
If it matters, this is how my "config/webpack/development.js" file looks like:
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
It depends on the webpack loader settings. If you are using css-loader as configured in react-scripts (as of 1.1.5), then the classNames are loaded using {modules: false} option, i.e. global styles, which can be referenced as strings in JSX code:
import "./styles.css";
... className="container" ...
Or you can load local styles using following CSS-file syntax:
:local .container {...
Or edit your webpack.config.js appropriately (see https://github.com/webpack-contrib/css-loader#scope for the official documentation of various options).
seems like you didn't enable an option { modules: true } for css-loader in webpack config
take a look
webpack-contrib/sass-loader#206
https://github.com/webpack-contrib/css-loader#options
Taken from: https://github.com/facebook/create-react-app/issues/1350

Css Selector in Framework7 vue

i try to build an Cordova/Phonegap application using vue.js and the Framework7.
I find out how to use functions like "onClick" using the "v-on:click="OnClick" attribute in an html element. Framework7 has jquery already implemented in the dom.
But there is one question. How can i access the dom directly, so that i can select whole css classes with the jquery selector. Like:
$('.likeButton'). ?
In the offical framework7 i found something like this to access the dom with its functions:
this.$$ or this.Dom7
This is what i have already written down in the home.vue file:
<script>
//import Fonts-awesome Icons
import FontAwesomeIcon from '#fortawesome/vue-fontawesome'
import {} from '#fortawesome/fontawesome-free-solid'
import F7Icon from "framework7-vue/src/components/icon";
import F7PageContent from "framework7-vue/src/components/page-content";
import * as Framework7 from "framework7";
export default {
name: 'FAExample',
components: {
F7PageContent,
F7Icon,
FontAwesomeIcon
},
methods: {
clickit: function () {
console.log("hi");
//this is what i have tested, looking if i have access to dom
let $$ = this.$$;
console.log($$);
},
//this is what i want to use
$('.likebutton').on('click',function () {
})
}
}
</script>
Did any of you have an idea how this works?
I hope you can help me. I'm new with vue.js in combination with the framework7.
Thank's for your help :)
We can use all the DOM functions just like
this.$$('.classname)
for example, if you want to hide something by jquery you can use as:
this.$$('.classname).hide()
To check all the DOM functions you can check the official documentation.
https://framework7.io/docs/dom7.html
But make sure that your DOM function should not in any Window function.
If you get the error to implemented it, just make the 'this' instance first.
Just like:
var self=this; // a global variable with this instance
use
self.$$('.classname).hide()
for any framework7 help, just ping me on skyp: sagardhiman5_1
Have you tried using Vue's $refs? You can set a reference to a specific DOM element and then access that in Vue.
A simple example:
<template>
<div class="some-item" ref="itemRef">Some item</div>
</template>
Then in the component:
var myItem = this.$refs.myItem;
// do what you want with that DOM item...
You can also access $refs from the parent. The example in the link below gives details on that.
More on $refs: https://v2.vuejs.org/v2/guide/components.html#Child-Component-Refs

Meteor React - Why are React Components defined differently in React Mounter vs React Layout from Kadira?

I am looking forward to Meteor 1.3 so I can import React components instead of having them as globals.
Been following this tutorial (https://voice.kadira.io/getting-started-with-meteor-1-3-and-react-15e071e41cd1) and I noticed I will have to use React-mounter instead of React-Layout from Kadira
In these docs here:
https://github.com/kadirahq/react-mounter
I see that the React components are defined like this:
const MainLayout = ({content}) => (
<div>
<header>
This is our header
</header>
<main>
{content}
</main>
</div>
);
Instead of something like this
MainLayout = React.createClass({
propTypes: {
content: React.PropTypes.element
},
render() {
return (
<div>
<header>
This is our header
</header>
<main>
{this.content}
</main>
</div>
);
}
});
Can you help explain to me what is happening here? Also how do I use this new style? Where to define all the properties, methods, mixins, etc?
Also as a side question, I noticed React was added as an npm package, instead of using Meteor add react. Is this how we are supposed to add react now?
You could categorize your components in two types: containers and presentational components.
For more details see this
React v0.14 introduced something called functional components which are presentation components that are created via a function instead of a class instance.
Since they are presentational components they are not intended to have more methods or mixins or anything, they just display data.
If you want to stick with React v0.14 and ES2015 you could create your components like
class Component extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps', nextProps.data.bar);
}
render() {
return <div>Bar {this.props.data.bar}!</div>;
}
}
You now have a full component that can have state, other event handlers and other methods.
A very important thing to note here is that the ES2015 syntax does not allow mixins because they prefer inheritance or functional composition.
Hope that helps!
Sorry I can't help you with your side question, haven't use React with Meteor.

Resources