css modules query breaks css rules with latest css-loader - webpack-4

With css-loader
{
test: /\.s?css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader',
query: {
modules: true,
localIdentName: '[name]-[local]-[hash:base64:8]'
}
},
{ loader: 'sass-loader'}
]
}
configured that way the css-loader seems to not find css rules under class names. The css rules listed under div.profile doesn't get applied on the screen. The css-loader ver. 1.0.0 in my code runs with Node 10.x. Switching modules: false gets the desired styling to show.
The code is posted below.
main.js:
require('babel-runtime/regenerator');
require('babel-register');
require('webpack-hot-middleware/client?reload=true');
require('./index.html');
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack 4</title>
</head>
<body>
<div class="profile">
<img src="./images/400.jpg" alt="">
<h1>Hello Webpack 4</h1>
<div id="react-root"></div>
</div>
<script src="/main-bundle.js"></script>
</body>
</html>
app.js:
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './counter';
import { AppContainer } from 'react-hot-loader';
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById('react-root')
);
};
render(Counter);
if (module.hot) {
module.hot.accept('./counter', () => {
render(require('./counter'));
});
}
counter.js:
import React, { Component } from 'react';
import { hot } from 'react-hot-loader';
import { css } from 'emotion';
import styled from 'react-emotion';
import styles from './main.scss';
const Fancy = styled('h1')`
color: ${props => props.wild ? 'hotpink' : 'gold'}
`;
const red = '#f00';
const className = css`
color: ${red};
font-size: 3rem;
`;
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.addCount = this.addCount.bind(this);
}
addCount() {
this.setState(() => ({ count: this.state.count + 1 }));
}
render() {
const isWild = this.state.count % 2 === 0;
return (
<div className={styles.counter}>
<h1 onClick={this.addCount} className={className}>Count: {this.state.count}</h1>
<Fancy wild={isWild}>react-emotion lib allows to hook styles to component names</Fancy>
</div>
);
}
}
export default hot(module)(Counter);
main.scss:
body {
background-color: #a1b2c3;
}
.profile {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
img {
border-radius: 50%;
box-shadow: 0 0 20px #000;
}
h1 {
font-family: 'source-code-pro', 'sans-serif';
font-weight: 400;
}
}
.counter {
border: 3px solid green;
}

The reason was the .profile class name in index.html is outside the counter.js scope. The css modules produce class names by the localIdentName pattern but the .profile class name was hard coded in index.html before css modules in counter.js came into play.
In counter.js
import styles from './main.scss';
console.log('styles:', styles);
outputs
styles: Object { profile: "main-profile-2P-yNf0J", counter: "main-counter-Pmp5YERO" }
How to get the main-profile-2P-yNf0J class name to index.html remains unclear for me.

Related

How do I detect change Sveltekit state

I am trying to add a class to an element in one component (nav) when an animation in a different component (logo) ends. In my $lib/Logo.svelte file I have the following code:
<script>
import { onMount } from 'svelte';
import { isLogoAnimationEnded } from './stores';
onMount(() => {
const body = document.querySelector('body');
const h1 = document.querySelector('.name');
h1?.addEventListener('animationend', () => {
isLogoAnimationEnded.update((n) => (n = true));
console.log($isLogoAnimationEnded);
body?.classList.add('shake');
});
return () => {
h1?.removeEventListener('animationend', () => {
isLogoAnimationEnded.set(false);
body?.classList.remove('shake');
});
};
});
</script>
<div class="logo-wrapper">
<h1 class="name">Tim Smith</h1>
<p class="title">Full Stack Web Engineer</p>
</div>
<style>
...
</style>
My
store is defined in $lib/stores.js:
import { writable } from 'svelte/store';
export const isLogoAnimationEnded = writable(false);
What I am trying to do is listen for a change in isLogoAnimationEnded in $lib/Nav.svelte and add a class to nav when isLogoAnimationEnded becomes true.
<script lang="ts">
import { onMount } from 'svelte';
import { isLogoAnimationEnded } from './stores';
let nav;
onMount(() => {
nav = document.querySelector('nav');
console.log('nav', nav);
});
if ($isLogoAnimationEnded) {
nav?.classList.add('fly-down');
}
</script>
<div class="nav-wrapper">
<nav aria-label="Main">
<ul>
<li>About</li>
<li>Projects</li>
<li>Contact</li>
</ul>
</nav>
</div>
<style>
...
</style>
My current setup does not work. Please help.
The code below is only going to run once
if ($isLogoAnimationEnded) {
nav?.classList.add('fly-down');
}
So I would delete it and write the class attribute on nav element like this:
<nav class={$isLogoAnimationEnded ? "fly-down" : ""} aria-label="Main">
This also gets rid of the onMount, nav variable and querySelector

Render normalize.css + emotion styles with Next.js

I'm trying to add Normalize.css as global and use emotion for my CSS Modules.
First my .babelrc
{
"presets": [
["env", {
"modules": false,
"useBuiltIns": true
}],
"next/babel"
],
"plugins": [
"syntax-dynamic-import",
"transform-runtime",
"transform-decorators-legacy",
"transform-class-properties",
"transform-object-rest-spread",
"es6-promise",
["module-resolver", {
"root": ["./src"],
"alias": {
"styles": "./styles",
"assets": "./assets",
},
"cwd": "babelrc"
}],
["inline-import", { "extensions": [".css"] } ],
["emotion", { "inline": true }]
]
}
Adding Normalize.css
In my _document.js I added the normalize
import Document, { Head, Main, NextScript } from 'next/document';
import normalize from 'normalize.css/normalize.css';
import { extractCritical } from 'emotion-server';
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const page = renderPage();
const styles = extractCritical(page.html);
return { ...page, ...styles };
}
constructor(props) {
super(props);
const { __NEXT_DATA__, ids } = props;
if (ids) {
__NEXT_DATA__.ids = ids;
}
}
render() {
return (
<html>
<Head>
<title>SSR</title>
<style jsx global>{normalize}</style>
<style dangerouslySetInnerHTML={{ __html: this.props.css }} />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
Same as shown here
Addin my css modules with Emotion
import React, { Component } from 'react';
import Breadcrumb from 'components/Breadcrumb';
import Link from 'next/link';
import styled, { hydrate, keyframes, css, injectGlobal } from 'react-emotion';
// Adds server generated styles to emotion cache.
// '__NEXT_DATA__.ids' is set in '_document.js'
if (typeof window !== 'undefined') {
hydrate(window.__NEXT_DATA__.ids);
}
const basicStyles = css`
background-color: white;
color: cornflowerblue;
margin: 3rem 0;
padding: 1rem 0.5rem;
`
const Basic = styled.div`
${basicStyles};
`
export default class extends Component {
render() {
return (
<Basic>
<p>Basic style rendered by emotion</p>
</Basic>);
}
}
Same as shown here
Problem
Error: StyleSheet: insertRule accepts only strings.
at invariant (/home/riderman/WebstormProjects/tmp/node_modules/styled-jsx/dist/lib/stylesheet.js:274:11)
at StyleSheet.insertRule (/home/riderman/WebstormProjects/tmp/node_modules/styled-jsx/dist/lib/stylesheet.js:125:7)
at /home/riderman/WebstormProjects/tmp/node_modules/styled-jsx/dist/stylesheet-registry.js:88:29
at Array.map (native)
at StyleSheetRegistry.add (/home/riderman/WebstormProjects/tmp/node_modules/styled-jsx/dist/stylesheet-registry.js:87:27)
at JSXStyle.componentWillMount (/home/riderman/WebstormProjects/tmp/node_modules/styled-jsx/dist/style.js:58:26)
at resolve (/home/riderman/WebstormProjects/tmp/node_modules/react-dom/cjs/react-dom-server.node.development.js:2616:12)
at ReactDOMServerRenderer.render (/home/riderman/WebstormProjects/tmp/node_modules/react-dom/cjs/react-dom-server.node.development.js:2746:22)
at ReactDOMServerRenderer.read (/home/riderman/WebstormProjects/tmp/node_modules/react-dom/cjs/react-dom-server.node.development.js:2722:19)
at renderToStaticMarkup (/home/riderman/WebstormProjects/tmp/node_modules/react-dom/cjs/react-dom-server.node.development.js:2991:25)
Added
Check source code here
https://gitlab.com/problems/test-emotion-plus-global-nextjs
Looks like there's an issue for this over on Zeit's styled-jsx page: https://github.com/zeit/styled-jsx/issues/298
According to this issue it is either external styles or that you need to add the css tag to your template literals.
Looking at your code you are using the css tag and don't see any externals styles that would be causing this. If you don't get a definite answer I'd say to follow up on issue 298 with Zeit. HTH, cheers!
Edit
Get rid of the jsx styles in there and just add normalize to your global template string:
injectGlobal`
${normalize}
html, body {
padding: 3rem 1rem;
margin: 0;
background: papayawhip;
min-height: 100%;
font-family: Helvetica, Arial, sans-serif;
font-size: 24px;
}
`;
I have an additional answer to this that works well in TypeScript and NextJS 9. It also keeps your import directly based on your node_modules.
Import raw-loader for the module:
yarn add raw-loader
In a global.d.ts, define a hook for raw-loader
declare module '!!raw-loader!*' {
const contents: string;
export = contents;
}
In a component called Meta I have inside my _document.tsx ( _app.tsx would be fine too, but _document ensures SSR), I have this
import normalizeCss from '!!raw-loader!normalize.css';
const Meta = () => (
<div>
<Global
styles={css`
${normalizeCss}
body {
// ...
}
`}
></Global>
</div>
);
export default Meta;

CSS modules - print stylesheet - how to override classes

How to create a print stylesheet which can override the dynamic styles created by css modules ?
Using CSS modules, classnames render with unique names like so :
<button class="buttons_style_primary-button__3T" type="submit"> Submit</button>
In my print stylesheet I have the following, which has no effect :
#media print {
button {
display: none;
}
}
I can get it to work by adding !important to the button style, but I will have many print styles and I don't want to do this for each style attribute. Is there an alternative ?
I'm also using React if there happens to be a React specific approach here.
Wound up doing the following :
Use !important for globals that need to override local values:
/* app.css */
#media print {
#page {
margin: 1cm;
}
* {
color: #000 !important;
}
}
Put component specific overrides into a style.css for each component:
/* style.css */
.my-class {
composes: rounded-corners from 'shared/ui.css';
margin: 0 0 60px 0;
background-color: white;
}
#media print {
.my-class {
page-break-inside: avoid;
font-size: 10px;
}
}
/* my-component.jsx */
import style from './style.css';
const MyComponent = () => {
return (
<div className={style.myClass}>
....
</Link>
);
};
There's also a third option which I haven't really tried.
You should be able to apply both the top-level override classname with your local classname using the classNames library:
import app from 'app.css';
import styles from './style.ss'
const MyComponent = () => {
return (
<div className={classNames(style.local, app.global)}>
....
</Link>
);
};
( this third option is just off the top of my head, I don't know if it will work )
Instead of this:
#media print {
.button {
display: none;
}
}
Try this:
.button{
#media print {
display: none;
}
}

Scrollable Foundation Section headers

Looking through http://foundation.zurb.com/docs/components/section.html, is there anyway I can add horizontal scroll for Section headers ( Tabs) . I am looking something like http://www.seyfertdesign.com/jquery/ui.tabs.paging.html in foundation sections with horizontal scroll and continue to use accordion in small screen
I found a solution for those interested : https://codepen.io/gdyrrahitis/pen/BKyKGe
.tabs {
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
.tabs-title {
float: none;
display: inline-block;
}
}
if someone needs an angularjs with jquery implementation, below code can help you, for pure jquery replace angularjs directive method with a native js method with respective attributes.
I tried to search for similar implementation but found nothing, so I have written a simple angular directive which can transform a foundation CSS tabs to scrollable tabs
angular.module("app.directives.scrollingTabs", [])
.directive("scrollingTabs", ScrollingTabsDirective);
//#ngInject
function ScrollingTabsDirective($timeout, $window) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if(attr.scrollingTabs == "true"){
element.addClass('scrolling-tabs-container');
element.find('.nav-buttons').remove();
element.append('<div class="scrolling-tabs nav-buttons nav-buttons-left"></div>');
element.append('<div class="scrolling-tabs nav-buttons nav-buttons-right"></div>');
let scrolledDiv = $(element).find('.tabs');
let scrolled;
let scrolling;
let scrollFn = (step, animationTime, cb) => {
scrolled = Math.max(scrolled + step, 0);
scrolledDiv.animate({
scrollLeft: scrolled
}, animationTime, ()=>{
if (scrolling) {
scrollFn(step, animationTime, cb)
}else{
if(cb){cb()}
}
});
};
let checkActiveNavButtonsClasses = () => {
scrolled = scrolledDiv.scrollLeft();
let scrollWidth = scrolledDiv.get(0).scrollWidth;
let scrolledDivWidth = scrolledDiv.get(0).clientWidth;
if(scrollWidth > scrolledDivWidth){
element.addClass('nav-active');
scrollWidth = scrolledDiv.get(0).scrollWidth;
if(scrolled == 0){
element.removeClass('nav-active-left').addClass('nav-active-right')
}else if(scrolled > 0 && scrolled + scrollWidth < scrolledDivWidth){
element.addClass('nav-active-left').addClass('nav-active-right');
}else if(scrolled > 0 && scrolled + scrollWidth >= scrolledDivWidth){
element.addClass('nav-active-left').removeClass('nav-active-right');
}else{
element.removeClass('nav-active-left').removeClass('nav-active-right')
}
}else{
element.removeClass('nav-active-left').removeClass('nav-active-right').removeClass('nav-active');
}
};
let scrollToActiveTab = () => {
let activeDD = scrolledDiv.find('dd.active');
let tabsOffset = scrolledDiv.offset();
let activeTaboffset = activeDD.offset();
let activeTabwidth = activeDD.width();
let scrolledStep = activeTaboffset.left - tabsOffset.left - scrolledDiv.width() + activeTabwidth;
scrollFn(scrolledStep, 100, checkActiveNavButtonsClasses);
};
element.find(".nav-buttons.nav-buttons-left")
.off("click.scrolling")
.on("click.scrolling", (event)=>{
event.preventDefault();
scrolling = false;
scrollFn(-100, 100, checkActiveNavButtonsClasses);
})
.off("mouseover.scrolling")
.on("mouseover.scrolling", function (event) {
scrolling = true;
scrollFn(-2, 1, checkActiveNavButtonsClasses);
})
.off("mouseout.scrolling")
.on("mouseout.scrolling", function (event) {
scrolling = false;
});
element.find(".nav-buttons.nav-buttons-right")
.off("click.scrolling")
.on("click.scrolling", (event)=>{
event.preventDefault();
scrolling = false;
scrollFn(100, 100, checkActiveNavButtonsClasses);
})
.off("mouseover.scrolling")
.on("mouseover.scrolling", function (event) {
scrolling = true;
scrollFn(2, 1, checkActiveNavButtonsClasses);
})
.off("mouseout.scrolling")
.on("mouseout.scrolling", function (event) {
scrolling = false;
});
$timeout(()=>{
checkActiveNavButtonsClasses();
scrollToActiveTab()
},1000);
$($window).off('resize.scrolling').on('resize.scrolling', _.debounce(()=> {
checkActiveNavButtonsClasses();
}, 500));
scope.$on('$destroy', function() {
$($window).off('resize.scrolling');
});
}
}
}}
css:
.scrolling-tabs-container {
position: relative;
.tabs {
overflow-x: hidden;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
display: block;
margin-right: 18px;
dd {
display: inline-block;
float: none;
margin: 0px -3px 0px 0px;
}
.tabs-title {
float: none;
display: inline-block;
}
}
.scrolling-tabs {
&.nav-buttons {
display: none;
position: absolute;
width: 19px;
height: 38px;
border: 1px solid #c1c1c1;
top: 1px;
background-color: rgba(255, 255, 255, 0.5);
opacity: 0.4;
cursor: pointer;
&:hover {
opacity: 1;
&:before {
color: #444;
}
}
&:before {
position: absolute;
left: 7px;
top: 8px;
color: #777;
}
&.nav-buttons-left {
left: 0;
&:before {
content: '<';
}
}
&.nav-buttons-right {
right: 18px;
&:before {
content: '>';
}
}
}
}
&.nav-active{
.tabs{
margin-right: 36px;
margin-left: 18px;
}
.scrolling-tabs {
&.nav-buttons {
display: inline-block !important;
}
}
}
&.nav-active-left{
.scrolling-tabs{
&.nav-buttons-left{
opacity: 0.8;
}
}
}
&.nav-active-right{
.scrolling-tabs{
&.nav-buttons-right{
opacity: 0.8;
}
}}}
HTML: Foundation Tabs template.
<tabset class="list-tabs" scrolling-tabs="true">
<tab heading="tab1"></tab>
<tab heading="tab2"></tab>
<tab heading="tab2"></tab>
</tabset>
Before you start you'll want to verify that both jQuery (or Zepto) and foundation.js are available on your page. These come with foundation package so just uncomment them in your footer or include them accordingly.
<div class="section-container auto" data-section>
<section class="active">
<p class="title" data-section-title>Section 1</p>
<div class="content" data-section-content>
<p>Content of section 1.</p>
</div>
</section>
<section>
<p class="title" data-section-title>Section 2</p>
<div class="content" data-section-content>
<p>Content of section 2.</p>
</div>
</section>
</div>
The foundation documentation has all of the information for this :
http://foundation.zurb.com/docs/components/section.html#panel2
This will get you your section tabular headers. You then want to manage the content to be scrollable.
<div class="content" data-section-content>
<p>Content of section 1.</p>
</div>
This content here will be the area to work on, try adding a new class called .scrollable
Within this class use something like:
.scrollable{
overflow:scroll;
}
You may want to add some more to this however this will get you started. Your HTML should now look like this :
<div class="content scrollable" data-section-content>
<p>Content of section 1. This content will be scrollable when the content has exceeded that of the div size. </p>
</div>
This this is what you are looking for.

Group nodes together in Cytoscape.js

I am using Cytoscape for network visualization.
I have found this example of grouped nodes that you can expand and contract.
My question is, is it possible to highlight nodes and then dynamically add/remove them to a group on an ad-hoc basis?
I am yet to find an examples online of someone actually trying this and all the examples I have found like the aforementioned already have the groups predefined in the initial data load.
You can use the drag and drop extension of cytoscape.
Also, there is the clipboard extension, where you can select multiple elements via ctrl + mousedrag.
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
style: [{
selector: 'node',
style: {
'label': 'data(id)'
}
},
{
selector: 'node:parent',
style: {
'label': ''
}
},
{
selector: 'edge',
style: {
'curve-style': 'bezier',
'target-arrow-shape': 'triangle'
}
},
{
selector: '.cdnd-grabbed-node',
style: {
'background-color': 'red'
}
},
{
selector: '.cdnd-drop-sibling',
style: {
'background-color': 'red'
}
},
{
selector: '.cdnd-drop-target',
style: {
'border-color': 'red',
'border-style': 'dashed'
}
}
],
elements: {
nodes: [{
data: {
id: 'a'
}
},
{
data: {
id: 'b'
}
},
{
data: {
id: 'c'
}
},
{
data: {
id: 'd',
parent: 'p'
}
},
{
data: {
id: 'p'
}
}
],
edges: [
]
}
});
var cdnd = cy.compoundDragAndDrop();
var removeEmptyParents = false;
var isParentOfOneChild = function(node) {
return node.isParent() && node.children().length === 1;
};
var removeParent = function(parent) {
parent.children().move({
parent: null
});
parent.remove();
};
var removeParentsOfOneChild = function() {
cy.nodes().filter(isParentOfOneChild).forEach(removeParent);
};
// custom handler to remove parents with only 1 child on drop
cy.on('cdndout', function(event, dropTarget) {
if (removeEmptyParents && isParentOfOneChild(dropTarget)) {
removeParent(dropTarget);
}
});
// custom handler to remove parents with only 1 child (on remove of drop target or drop sibling)
cy.on('remove', function(event) {
if (removeEmptyParents) {
removeParentsOfOneChild();
}
});
// toggle check handler
document.getElementById('remove-1ch-parents').addEventListener('click', function() {
removeEmptyParents = !removeEmptyParents;
if (removeEmptyParents) {
removeParentsOfOneChild();
}
});
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px;
}
#cy {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 1;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
#options {
z-index: 2;
position: absolute;
right: 0;
top: 0;
margin: 0.5em;
}
<head>
<title>cytoscape-compound-drag-and-drop demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-compound-drag-and-drop#1.0.0/cytoscape-compound-drag-and-drop.min.js"></script>
</head>
<body>
<h1>cytoscape-compound-drag-and-drop</h1>
<div id="cy"></div>
<div id="options">
<input id="remove-1ch-parents" type="checkbox" value="false" />
<label for="remove-1ch-parents">Remove parents with only one child</label>
</div>
</body>
The question itself is not that clear requirenment-wise, so if there is still something unclear, please edit it to be more precise (and maybe your code so that we can use that as a base. THX