Targeting Pure elements in next.js with CSS modules - javascript

I'm porting an app from React to Next and I keep getting an error with my css: "ul" is not pure (pure selectors must contain at least one local class or id).
I'm using css modules and am not sure what the workaround is to keep everything in the css module.
for example:
index.js
<div className={styles.container}>
<ul>
<li>Person One</li>
</ul>
</div>
index.module.css
.container{
background-color: pink;
}
ul{
list-style-type: none; //this throws an error
}

If you don't want to use classes as mentioned #Zeeshan's answer, you cannot use the module-based CSS. You will need to use a global stylesheet to target pure elements as selectors. They cannot be component-scoped.
Further, you can only import a global stylesheet in one place (_app.tsx/_app.jsx/_app.js), or you'll receive an error (explained here).
You may break your styles out across many stylesheets, but they all have to be global, so there's probably little benefit from doing so.
To quote directly from this Next.js GitHub discussion forum:
You are receiving this error because you are using html tags directly instead of classnames or ids in a file extension that is probably [filename].module.(css | scss | sass)
File extensions with *.module.(css | scss | sass) are css modules and they can only target elements using classnames or ids and not using tag names. Although this is possible in other frameworks like create-react-app, it is not possible in next-js.
My suggestion is using these html selector css in a separate file that doesn't contain the '.module' in it.
Example: [filename].(css | scss | sass) --> styles.scss
And instead of importing like this
import styles from './styles.module.scss';
import like this
import './styles.scss';

This is actually extremely simple.
You can add a container class to the top element (just like you did), and then use combinators ( ,+,>, etc.) to target pure elements as you wish.
For example:
index.js:
<div className={styles.container}>
<ul>
<li>Person One</li>
</ul>
</div>
index.module.css:
.container {
background-color: pink;
}
.container ul {
list-style-type: none;
}

Try giving a className or id to ul tag and then write your styles accordingly.
for example:
index.js
<div className={styles.container}>
<ul className={styles.container__list}>
<li>Person One</li>
</ul>
</div>
index.module.css
.container {
background-color: pink;
}
.container__list{
list-style-type: none;
}

Related

Return CSS selector for a React Component

I need to interact with an online application written by a third-party developer, part of which is written in React. To interact with the site I'm driving Firefox via Selenium and Python. I can use traditional CSS selectors to interact with most of the site, but the parts written in React have random autogenerated class names that change every time the devs rebuild their app. There are no IDs found on any DOM elements except for the root node.
How can I get a unique CSS Selector for a DOM element, given a specific React Component in the virtual DOM?
For instance, by using React Develper Tools I see this tree:
Context.Provider
├─ Context.Provider
│ └─ gs
├─ Context.Provider
│ └─ styled.div
│ └─ styled.button
└─Iv
How can I get a unique CSS Selector for the DOM element that represents the Context.Provider > Context.Provider > styled.div > styled.button React Component?
I have seen the React Element Selector Query NodeJS library, but I need something that can run in Python, or can be interpreted from within Python and Selenium.
Here is example code from the app I'm targeting:
<div id="root">
<div class="sc-hTtwUo kyYkJm">
<div class="sc-jqUVSM bZjAcv">
<button class="sc-jSMfEi kXusAw">Foo</button>
<button class="sc-eCYdqJ ddMHci">Bar</button>
</div>
<div class="sc-kgUAyh bqLSQt">
<div class="sc-kgflAQ kGhjap">
<div class="sc-kDDrLX civFbY">
... Lots more ...
</div>
</div>
</div>
</div>
</div>
Other than the button element text, that is actual production HTML. Note that this is not deliberate obfuscation, rather this is not uncommon for React apps.
Where do the React libraries store a mapping that X component is represented by Y DOM element? It is that mapping which I am trying to locate and read.
You can check for id, if ids are given to each tag then can be accessed very easily.
for instance
<div class="randon-class" id="renderingID">
This is some div rendering some content
<div>
You can access the div by #renderingID this will give you the required properties.
Usually, there is a pattern when a class name is transformed, like underscore. So you can split using that and check for given classname. For example, we are looking for .content under .body. So element will have className as body_content_<hash>. So, we can split using and check if it has content
Note: This approach is based on string manipulation. So if the classname has same delimiter, it will fail.
document
.getElementById('btnSearch')
.addEventListener('click', () => {
const searchClass = document.getElementById('txtSearchId').value
const elements = searchElements(searchClass)
highlightElement(searchClass, elements)
})
function searchElements(className) {
return document.querySelectorAll(`[class*="${className}"]`)
}
function highlightElement(className, elements) {
document
.querySelectorAll('.border')
.forEach((element) => {
element.classList.remove('border')
})
Array.from(elements).filter((element) => {
const classes = element.className.split(/[ _]/);
const matched = classes.includes(className);
if (matched) {
element.classList.add('border')
}
return matched
})
}
.border {
border: 1px dashed gray;
}
div {
margin: 5px;
}
<div>
<input type="text" id="txtSearchId" />
<button id="btnSearch">Search and Highlight </button>
</div>
<div class='body_1nu21'>
<div class='body_content_2nasiy'>
<div class='body_card_21dfl'>test</div>
<div class='body_card-test_2345a'>test</div>
<div class='body_card-blue_9hysh8'>test</div>
<div class='body_card_7hdsg'>test</div>
</div>
</div>
You probably have to go by structure / innerText in this case.
For example:
#root > div > div > button => Foo
#root > div > div > button + button => Bar

React Localize Global Styles?

Is it possible to localize the global styles of a module to only one file?
for instance, I would like to change the width of an item from a react module but only for one file. inline styles aren't possible :/
edit for context: in my particular case ... i'm using 'react-multi-carousel' ... and to go over the breakpoint behavior, I've set .react-multi-carousel-item { width: 310px !important; } . However i'd like this style to be applied only to one component and not my entire project (I'm using the carousel in more than one place). Any way to localize a global style (CSS file)?
You just nest the CSS rule to only apply when inside a specific container:
.my-container .react-multi-carousel-item { width: 310px !important; }
And then in React:
return <>
<MultiCarousel />
<div className="my-container">
<MultiCarousel />
</div>
</>
In that example the first <MultiCarousel /> would not see the extra style, but the second one would.
"is there any way to add css within the TSX file by any chance? If possible, I'd like to not have an external css file."
You could try a scoped style tag
For example:
return <>
<div>
<style scoped>{`
.react-multi-carousel-item {
width: 310px !important;
}
`}</style>
<MultiCarousel />
</div>
</>

html style selector remains after webpack postcss processing

I have added postcss loader with options to my vue js ui library project . I am using postcss loader so that when i import this npm package elsewhere the styles wont conflict. Vue CLI uses PostCSS internally.
my sample html:
<div id="navbackground ">
<h5>Sample h5 text</h5>
</div>
one of my css classes e.g. mycolors.css has following content in it:
#navbackground {
background: red;
}
h4{color:white}
configuration for postcss looks like below
const prefixer = require('postcss-prefixer')
css: {
loaderOptions: {
postcss: {
plugins: [
prefixer({
prefix: 'mystyles-'
})
]
}
}
},
And the css file generated after running web pack build looks like below and it applies prefix.
#mystyles-navbackground {
background: red;
}
h4{color:red}
AS you see from above h4 is NOT transformed with any prefix identifier and also stlyes(e.g. mystyles-navbackground) are not applied on html.
when i looked at the rendered html it has the same class id (navbackground) so it didn't apply the new style name (mystyles-navbackground)
<div id="navbackground ">
<h5>Sample h5 text</h5>
</div>
Do i need to run another loader or plugin to apply the new prefixed style names to html ?

In Vue.js, is there a way to keep component templates out of JavaScript strings?

I just worked through the Guide on Vue.js's website, and I have a bad feeling about templates for components. It seems strange to me that they are specified in strings; sure, maybe this works for very short templates, but once you get to multiline templates, you need to start escaping your new lines and it just feels wrong to have html in javascript strings to begin with. Not to mention that syntax highlighting or any other nice IDE features are useless with HTML in JS strings.
Two alternatives that are detailed in the docs are using inline templates, or X-templates, but both of these options are discouraged.
The only other alternative seems to be Single File Components, which seems like a good option, but they are in the Advanced section and in the docs, it is said that for small and medium sized apps, simply using Vue.component should be enough. Furthermore, Single File Components look like they're more difficult to integrate into a project, requiring tapping into the project's build system (the docs talk about Webpack and Browserify).
So I'm confused. Do I just need to accept that my component code is going to look as messy as this example, pulled straight from the docs?
Vue.component('currency-input', {
template: '\
<span>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
>\
</span>\
',
......
Given that you are starting a new project, you can use vue-hackernews-2.0 as boilerplate, where you see lot of components already coded with webpack integration for both dev and prod env. This is also developed by core vue team and recommended in official docs.
You can see there are different files for each component and one component looks like following having clear separation of HTML, JS and CSS part:
<template>
<li v-if="comment" class="comment">
<div class="by">
<router-link :to="'/user/' + comment.by">{{ comment.by }}</router-link>
{{ comment.time | timeAgo }} ago
</div>
<div class="text" v-html="comment.text"></div>
<div class="toggle" :class="{ open }" v-if="comment.kids && comment.kids.length">
<a #click="open = !open">{{
open
? '[-]'
: '[+] ' + pluralize(comment.kids.length) + ' collapsed'
}}</a>
</div>
<ul class="comment-children" v-show="open">
<comment v-for="id in comment.kids" :id="id"></comment>
</ul>
</li>
</template>
<script>
export default {
name: 'comment',
props: ['id'],
data () {
return {
open: true
}
},
computed: {
comment () {
return this.$store.state.items[this.id]
}
},
methods: {
pluralize: n => n + (n === 1 ? ' reply' : ' replies')
}
}
</script>
<style lang="stylus">
.comment-children
.comment-children
margin-left 1.5em
.comment
border-top 1px solid #eee
position relative
.by, .text, .toggle
font-size .9em
margin 1em 0
.by
color #999
a
color #999
text-decoration underline
.text
overflow-wrap break-word
a:hover
color #ff6600
pre
white-space pre-wrap
.toggle
background-color #fffbf2
padding .3em .5em
border-radius 4px
a
color #999
cursor pointer
&.open
padding 0
background-color transparent
margin-bottom -0.5em
</style>
This uses webpack for build and adds working config as well which I myself am using in production without any issue.
You can use <template>...</template> or <script type="text/x-template">...</script>, and specify the selector in template attribute for that.
<template id="myComponent">
<div>
<h1>Hello!</h1>
<p><slot></slot></p>
</div>
</template>
Vue.component('myComponent', {
template: '#myComponent'
})
Simple working example here: http://codepen.io/anon/pen/dNWrZG?editors=1010
Also, the build process of single file components is not that difficult. You can check the webpack-simple template: https://github.com/vuejs-templates/webpack-simple, the vue-loader will do everything for you.
Once you feel comfortable with webpack, you can take a look at the full webpack template: https://github.com/vuejs-templates/webpack
From my experiences, if the template is very short, use inline mode is OK. If not, x-template also allows you to get rid of escaping line breaks. I don't see why you think these approaches are discouraged. Can you provide more information?
However, if you insist to embed long template inline, you can still do that without escaping. The answer is ES6 template literals - string wrapped within ``:
template: `
<span>
$
<input
ref="input"
v-bind:value="value"
v-on:input="updateValue($event.target.value)"
>
</span>
`,
On the other hand, I really think vue-cli is a great tool. Webpack is something worth learning.
This is not really a direct answer (It's editor Specific) but i just thought i should share, but if you're using Visual Studio Code as your editor, Then you can keep your templates as part of your component and still get Syntax highlighting.
Thanks to this awesome extension
angular-2-inline
It was Originally meant for Angular 2, but it works for all ES6 Template strings (Provided the key of Template String is "template").
Single File components are the Recommended way, but they can become a burden when you're not building a Full-fledged SPA and you're just using Vue to enhance some HTML templates Here and there, and you don't want to get Involved with Webpack.
Don't believe it? I'm currently using it.
Thanks to #danidee answer I was able to track an Atom extension to properly do syntax highlighting for "backticked" HTML strings. However as he indicates it will only work when the template is declared inside the very component definition (and I want to separate template in another .js file). So I did this and it's a more ellegant solution, I think:
COMPONENT DEFINITION
/* template definition */
import { csFooterView } from './csfooter-view.js?v6';
/* component definition */
const csfooter = {
name: 'csfooter',
props: ['projectName'],
data: function() {
return {
copyrightCompany: 'CSDev'
}
},
template: csFooterView.template
};
/* exports */
export { csfooter };
TEMPLATE DEFINITION
/**
* VUE Footer Component
* Template
*/
/* template definition */
const csFooterView = {
template: `
<footer class="footer bg-warning p-4 m-auto">
<p>{{ copyrightCompany }} (c) 2020 - {{ projectName }}</p>
</footer>
`
};
export { csFooterView }
ATOM SCREENSHOT WITH HTML SYNTAX HIGHLIGHTING IN THE .JS FILE
Link to Atom extension

Using flexbox with angular 2 components

I want to update visual side(grid, colors) of my angular 2 application. Old layout was build with bootstrap 4, which I don't really need anymore. I decided to go for plain css3, and build grid with flexbox. I made a preview of this grid on codepen.
However implementation in project is hard and I am now stuck. Let's consider an example:
import {Component} from 'angular2/angular2';
#Component({
selector: 'some-component',
template: `
<aside class="col-4 main-aside"></aside>
<section class="main-section center"></section>
`
})
export class SomeComponent { }
If I bootstrap this component within, for example .container I may get this result:
<body>
<!-- .container is flex parent -->
<div class="container">
<some-component>
<!-- .main-aside and .main-section should be flex children -->
<aside class="main-aside"></aside>
<section class="main-section center"></section>
</some-component>
</div>
</body>
As you can see the flex chain parent -> child is broken because of component "selector" between them. I menaged to fix this by adding styles: display: flex; width: 100%; to component selector from chrome dev tools, however I don't know how to make this work from code perspective nor is it the best way to do so.
I would really appreciate any help, because I have no idea how to fix this with the exception of not using flexbox.
I am using angular 2.0.0-alpha.44
And yes, I am aware of angular's alpha state.
I fixed this issue by adding styles to component. For example:
:host {
display: flex;
}
:host {} was the key.
:host selector solves the problem but you end using it very often in many components.
I used instead the following global CSS:
.row > * {
display: flex;
flex-basis: 100%;
}
The angular team released their own flex-layout package, so you don't need to write it by your self anylonger:
https://github.com/angular/flex-layout

Categories

Resources