Running stylelint over custom styled-components template string - css

On a project we have custom templates string to abstract styled-components functions. For example, we have templates string for the media-queries that look like this one:
// Definition
const for1080p = (...args) => css`
#media (min-height: 1080px) {
${css(...args)}
}
`;
// Usage
const Logo = styled.div`
width: 200px;
${for1080p`
width: 300px;
`}
`;
We made this choice because it keeps the original styled syntax. It is also nicely formated by prettier.
Our main problem today is that we don't know how to analyse with stylelint our CSS that are inside a custom template string.
For example:
const Logo = styled.div`
widht: 200px; /* <--- Unexpected unknown property "widht" */
${for1080p`
widht: 300px; /* <--- No error detected :( */
`}
`;
Do you know how to do this?

The CSS-in-JS parser built into stylelint doesn't yet support interpolation tagging, and therefore can't know that the second widht: 300px; is a declaration.
Until that support is added, you can either:
see how much mileage you get from the styled-components stylelint processor and its support for interpolation tagging
use standard CSS constructs, rather than custom templates string, for things like #media

I think there is a way to do it now with https://github.com/styled-components/stylelint-processor-styled-components and as documentation states -
Combining with moduleName, importName and strict, you can tell the
processor what kinds of tagged template literals to lint.
{
"processors": [["stylelint-processor-styled-components", {
"moduleName": "styled-components",
"importName": ["default", "for1080p"], <---- here
}
That stuff wasn’t tested by me, but I think it suppose to do the trick

Related

styled-components - Parent component style overriding child style

I'm using styled-components with React. I'd like to remove the default bottom margin for all <p>s, so in my main App component I added a nested selector:
const App = styled.div`
p {
margin: 0;
}
`
However, I do want to be able to add margin to some <p>s, e.g.:
const SpacedP = styled.p`
margin-bottom: 10px;
`
But when I try to use a <SpacedP> within <App>, the resulting App CSS is more specific than the SpacedP CSS, so my <SpacedP>s still have no margin! (App CSS compiles to ${App} p { ... }, while SpacedP only has one prefix, ${SpacedP} { ... }).
Is there a common pattern for situations like these? I don't really like the &&& specificity workaround suggested here, seems ugly and hacky; hoping there's a more common setup that I'm missing.
Thanks!

How to create shared styles using styled components?

I am trying to achieve something like the following output using styled components (shared selectors).
.styleOne, .styleTwo {
color: blue; /* shared */
}
.styleOne {
font-size: 10px;
}
.styleTwo {
font-size: 20px;
}
I've tried:
1 - (this one makes sense why it doesn't render my desired output)
const shared = `color: blue;`
const StyleOne = styled.div`
font-size: 10px;
${shared}
`
const StyleTwo = styled.div`
font-size: 20px;
${shared}
`
2 -
const Shared = styled.div`
color: blue;
`
const StyleOne = styled(Shared)`
font-size: 10px;
`
const StyleTwo = styled(Shared)`
font-size: 20px;
`
3 -
const shared = css`
color: blue;
`
const StyleOne = styled.div`
font-size: 10px;
${shared}
`
const StyleTwo = styled.div`
font-size: 20px;
${shared}
`
All the above result in something like the following:
.styleOne {
color: blue; /* duplicated */
font-size: 10px;
}
.styleTwo {
color: blue; /* duplicated */
font-size: 20px;
}
Technically the styles are shared in each approach from a code perspective. However, the rules are still duplicated. I was hoping to be able to share the CSS rules themselves.
An analogy in SASS would be - I want to use a placeholder instead of a mixin.
To add some context on why I want to achieve this, I want to have a smaller HTML document for performance reasons. I'm imagining in a large app where I'm sharing a lot of styles, the style block may get huge.
The term large apps is too ambiguous to state whether or not you'll have performance issues -- whether it's from downloading/executing JS or downloading parsing CSS and/or fetching other media (fonts, images, etc). That said, styled-components applies styles in the head during run-time and does an excellent job of deferring them until they're required in the DOM.
Take a look at this example and follow these steps:
Inspect the DOM and within the Elements tab expand the head
Look for this style tag and expand it: <style data-styled="active" data-styled-version="5.2.0"></style>
With the inspector still open, navigate to other pages and notice how class names are generated on-the-fly
Once the classes have been assigned, they're then reused
This has the similar effect of using css-modules (scoped classes) without the extra request to download the required CSS, and it automatically prefixes CSS properties without any additional plugins.
So the question becomes: Is it better to have more Javascript (packaging and running styled-components in run-time) and less CSS (less requests to download CSS per page) or more CSS and potentially less Javascript? You'll need to run metrics -- like a lighthouse test -- in production to determine which is more performant; otherwise, it's just going to be a guess/personal opinion.
Either way, you're going to be running into some sort of render blocking, whether it's downloading/parsing CSS or downloading/executing JS.

Expand all PrimeNG Accordion panels automatically for Printing

I am currently using the PrimeNG library's accordion component in my angular project. See info here.
The template includes some special css styling for printing the page--something like the following:
#media print {
.profile-progress-bar, .top-template-header-content, .header.profile-header{
display: none !important;
}
html, body {
height: auto;
font-size: 10px !important;
}
p-accordionTab > div {
display: block !important;
selected: true !important;
}
}
What I am trying to do, is automatically expand all accordionTab elements when the #media print rendering is processed for the page to be printed.
From the documentation I see that each accordionTab element has a [selected] property which can be bound to and set to "true" in order to expand the tab.
Selected Visibility of the content is specified with the selected
property that supports one or two-way binding.
However, can this be somehow automatically triggered when the #media print rendering occurs?
Thanks!
media query is the way to go, you can take a css only approach to achieve this; no change in TS or HTML files
relevant css:
#media print {
::ng-deep .ui-accordion-content-wrapper-overflown {
overflow: visible;
height: auto !important;
}
}
complete demo on stackblitz here
This is an interesting one. To keep it inside the realm of Angular, you could use the #angular/cdk/layout library and inject MediaMatcher. You could also, of course, do almost this exact same thing using JavaScript (see here... the cdk/layout method I'll show you really just wraps this).
The MediaMatcher service has a method called matchMedia, and from there you just add a listener:
import { MediaMatcher } from '#angular/cdk/layout';
constructor(private readonly mediaMatcher: MediaMatcher ) { }
ngOnInit() {
mediaMatcher.matchMedia('print').addListener(e => e.matches ?
console.log('printing!') : null);
}
So where I've put the console.log, just perform your logic to get the accordians to expand.

CSS Specificity with CSS Module

First, let me say I understand that I have a custom component "Card" that I use in one of my routes, "Home".
Card.js
import s from 'Card.css';
class Card {
...
render(){
return (<div className={cx(className, s.card)}>
{this.props.children}
</div>);
}
}
Card.css
.card {
display: block;
}
Home.js
<Card className={s.testCard}>
...
</Card>
Home.css
.testCard { display: none; }
A problem I faced here, is that the card remained visible even though I set the display to none, because of seemingly random CSS ordering in the browser. This ordering did not change even if Javascript was disabled.
To get .testCard to correctly load after .card, I used "composes:":
Home.css
.testCard {
composes: card from 'components/Card.css';
display: none;
}
Apparently this causes css-loader to recognize that .testCard should load after .card. Except, if I disable Javascript, the page reverts back to the old behavior: the .card is loaded after .testCard and it becomes visible again.
What is the recommended way to get our page to prioritize .testCard over card that behaves consistently with or without Javascript enabled?
As I'm using CSS modules, charlietfl solution wouldn't really work as is. .card is automatically mangled to a name like .Card-card-l2hne by the css-loader, so referencing it from a different component wouldn't work. If I import it into the CSS file of Home.css, that also doesn't work, because it creates a new class with a name like .Home-card-lnfq, instead of referring to .Card-card-l2hna.
I don't really think there's a great way to fix this so I've resorted to being more specific using a parent class instead.
As an example, Home.js:
import s from 'Home.css';
import Card from './components/Card';
class Home {
...
render(){
return (
<div className={s.root}>
<Card className={s.testCard}>Hi</Card>
</div>
);
}
}
Home.css
.root {
margin: 10px;
}
.root > .testCard {
display: none;
}
This way, we don't need to know what class names component Card is using internally, especially since in cases like CSS Modules or styled components, the class name is some unique generated name.
I don't think I would have come to this solution if it wasn't for charlieftl's solution, so thank you very much for that.
Just make the testCard rule more specific by combining classes
.card {display: block;}
.card.testCard { display: none; }

How to override mixins in LESS CSS 1.4+

I've been using what I thought was a very elegant pattern for defining the styles of reusable components/widgets, using LESS. It works beautifully in LESS 1.3-, but after upgrading recently, my whole library is broken. Does anyone know a way to accomplish something like this in 1.4+?
Here's a very simple example of a component:
#componentName {
.loadMixins(){
.text() {}
.header() {}
}
.apply(){
> h3 {
// markup-specific styles
padding: 3px;
margin-bottom: 0;
// custom styles
.header();
}
> div.body, > div.popup p {
color: red;
// custom styles
.text()
}
}
}
And here's how it would be used:
.coolWidget {
#componentName.loadMixins();
// override mixins here
.text(){
color: green;
}
#componentName.apply();
}
This keeps all the markup-dependent styles abstracted from the user. I could completely change my markup and the user's styles would still work. According to the less.js changelog, 1.4.0 Beta 1 has a line "variables in mixins no longer 'leak' into their calling scope"
Is there any way around this?
Strictly speaking nested variables and mixins are still expanded into calling scope unless this scope already has those names defined.
Your example above results in a error:
SyntaxError: .header is undefined...
and it's expected as no .header() is actually defined within the .coolWidget (or anywhere else).
This can be fixed by providing "default" definitions for .text and .header somewhere inside #componentName.
For example if you modify .loadMixins() to:
.loadMixins() {
.text();
.header();
// default properties in case a caller does not provide its own:
.text() {}
.header() {}
}
then the example compiles OK and all text/header properties are overridden as expected.
I can imagine how your library may become broken because of new scope rules but this particular example you gave above does not illustrate the problem.

Resources