Calling a Stylus {block} with a variable in an iteration - css

I'm trying to generate classes with Stylus {block} insertions while on an iteration with that code:
// Blocks
flexbox =
display flex
inline-flexbox =
display inline-flex
// Properties collection
props = {
'flexbox': 'flex',
'inline-flexbox': 'inline-flex'
}
// Generating classes
for kProp, vProp in props
.u-{vProp}
{kProp}
{kProp} is supposed to output {flexbox} and {inline-flexbox} but I guess there's some kind of syntax conflict between calling a Stylus {block} and calling the iteration variable.
So basically with this code, I got no output at all.
I also tried to escape the { } but no luck.
Does anyone know a workaround?

No proper solution but some workarounds I found:
You can replace {blocks} by extending $placeholders but be aware that with Stylus you can't extend inside a Media Query (it just ignore the MQ part)
You can simply replace {blocks} by mixins() (and it works inside Media Queries) which is the one I chose.
So basically now my code looks like this:
// Blocks
flexbox()
display flex
inline-flexbox()
display inline-flex
// Properties collection
props = {
'flexbox': 'flex',
'inline-flexbox': 'inline-flex'
}
// Generating classes
for kProp, vProp in props
.u-{vProp}
{kProp}()

Related

Tailwindcss + Nextjs problems with class + variables

Good afternoon, I need help with a big problem I'm having with tailwindcss + nextjs...
So the problem is when it comes to setting the classes, I need to use a variable, the class is set in the css, but the tailwind is not converting the class into a style.
I need it to be like this:
I already tried to set the class as constant, I tried to set the constant both inside the component and in getstaticprops, and none of them worked.
I've tried to set a class within the css itself and it didn't work either.
Tailwind uses regex to find class names, and because of this they need to exist as unbroken strings in your source code. A consequence of this is you cannot use string interpolation the way you're trying to do, as Tailwind will not be able to find the class name.
What you can do instead is map your props to static class names:
const Component = ({pokemon}) => {
const pokemonBgVariants = {
pikachu: 'bg-pokemontype-pikachu',
bulbasaur: 'bg-pokemontype-bulbasaur',
// ...
}
return (
<div className=`[...other classes] ${pokemonBgVariants[pokemon.types[0].type.name]}`></div>
)
}

How to dynamically add classes to NextJS component using class names from CMS?

I am looking for a solution that will allow some styling options in a CMS that can dynamically change the classes of specific components in my NextJS app. My code looks like this:
pages/index.js:
...
import client from "../lib/client";
const Home = ({ headerConfig }) => {
return (
<>
<Header headerConfig={headerConfig} />
...
</>
);
};
export const getServerSideProps = async () => {
const headerConfig = await client.getDocument("headerConfig");
return {
props: { headerConfig },
};
};
export default Home;
components/Header.jsx:
const Header = ({ headerConfig }) => {
return (
<nav className={`relative ... ${headerConfig.bgColour}`}>
...
</nav>
);
}
export default Header
However, the styling does not apply and the background colour remains unchanged although the class does seem to be injected into the class attribute on the browser.
I know my current method is incorrect but I am clueless as to how to fix this. Could someone help point me in the right direction?
I assume that you are using tailwind. If so, you cannot inject classnames into an html element. This is because tailwind only includes classes that are explicitly declared somewhere within your code (it will find classes within any string in your project depending on the configuration). You can get around this problem by adding classes to the safelist array in your tailwind.config.js file. You can also safelist classes with regex to allow all variants of certain utilities.
However, safelisting only works if there are a specific set of classes that could potentially be injected. One option, which will be guaranteed to work but NOT RECOMMENDED, is to add a <link> in your html to the tailwind cdn. However this will include every single tailwind class in your css bundle, making it MUCH larger and your website slower.
Another solution is to use inline styles which are calculated with javascript depending on the classes you need to inject. If you are dealing with only simple parts of tailwind (like padding, margin, or other sizing units), this may be a good approach. For example a class like p-4 would get converted to padding: 1rem in your inline styles.
Depending on the needs of your application, one of these three approaches is probably the way to go. Hope this helps!

SASS/SCSS variable not working with CSS variable assignment

I have the following SCSS code:
#mixin foo($bar: 42) {
--xyzzy: $bar;
}
bar {
#include foo;
}
I would expect that I get CSS variable --xyzzy set to 42 on all bar elements. Instead of this, I get CSS stating bar { --xyzzy: $bar; }. The variable was not interpreted. I would need to use #{…} syntax instead to get the variable set.
Is this a feature of the SCSS/SASS? A bug? Can I get the interpretation working without enclosing the variable name in #{…}?
Actual result:
bar {
--xyzzy: $bar;
}
Expected:
bar {
--xyzzy: 42;
}
It's not a bug, it's how the Sass compiler works regarding CSS custom properties, known as CSS variables. The syntax #{…} is called interpolation, and it is the only way to inject dynamic values into a custom property. Here is a quote from the doc:
CSS custom properties, also known as CSS variables, have an unusual declaration syntax: they allow almost any text at all in their declaration values. What’s more, those values are accessible to JavaScript, so any value might potentially be relevant to the user. This includes values that would normally be parsed as SassScript.
Because of this, Sass parses custom property declarations differently than other property declarations. All tokens, including those that look like SassScript, are passed through to CSS as-is. The only exception is interpolation, which is the only way to inject dynamic values into a custom property.
That's the reason why you have that behavior, and only doing so works:
#mixin foo($bar: 42) {
--xyzzy: $bar; // does not work
--xyzzy: #{$bar}; // works
}

How would I apply Material-UI managed styles to non-material-ui, non-react elements?

I have an application where I'm using Material UI and its theme provider (using JSS).
I'm now incorporating fullcalendar-react, which isn't really a fully fledged React library - it's just a thin React component wrapper around the original fullcalendar code.
That is to say, that I don't have access to things like render props to control how it styles its elements.
It does however, give you access to the DOM elements directly, via a callback that is called when it renders them (eg. the eventRender method).
Here's a basic demo sandbox.
Now what I'm wanting to do is make Full Calendar components (eg, the buttons) share the same look and feel as the rest of my application.
One way to do this, is that I could manually override all of the styles by looking at the class names it's using and implementing the style accordingly.
Or - I could implement a Bootstrap theme - as suggested in their documentation.
But the problem with either of these solutions, is that that:
It would be a lot of work
I would have synchronisation problems, if I made changes to my MUI theme and forgot to update the calendar theme they would look different.
What I would like to do is either:
Magically convert the MUI theme to a Bootstrap theme.
Or create a mapping between MUI class names and the calendar class names, something like:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
I wouldn't mind having to massage the selectors etc to make it work (ie. For example - MUI Buttons have two internal spans, whereas Full Calendar have just one). It's mostly about when I change the theme - don't want to have to change it in two places.
Using something like Sass with its #extend syntax would is what I have in mind. I could create the full-calendar CSS with Sass easily enough - but how would Sass get access to the MuiTheme?
Perhaps I could take the opposite approach - tell MUI 'Hey these class names here should be styled like these MUI classes'.
Any concrete suggestions on how I would solve this?
Here is my suggestion (obviously, it's not straight forward). Take the styles from the MUI theme and generate style tag based on it using react-helmet. To do it event nicely, I created a "wrapper" component that do the map. I implemented only the primary rule but it can be extended to all the others.
This way, any change you will do in the theme will affect the mapped selectors too.
import React from "react";
import { Helmet } from "react-helmet";
export function MuiAdapter({ theme }) {
if (!theme.palette) {
return <></>;
}
return (
<Helmet>
<style type="text/css">{`
.fc-button-primary {
background: ${theme.palette.primary.main}
}
/* more styles go here */
`}</style>
</Helmet>
);
}
And the use of the adapter
<MuiAdapter theme={theme} />
Working demo: https://codesandbox.io/s/reverent-mccarthy-3o856
You could create a mapping between MUI class names and the calendar class names by going through ref's. It's possible that this is not what some would call "best practice"...but it's a solution :). Note that I updated your component from a functional component to a class component, but you could accomplish this with hooks in a functional component.
Add refs
Add a ref to the MUI element you want to set as a reference, in your case the Button.
<Button
color="primary"
variant="contained"
ref={x => {
this.primaryBtn = x;
}}
>
And a ref to a wrapping div around the component you want to map to. You can't add it directly to the component since that wouldn't give us access to children.
<div
ref={x => {
this.fullCal = x;
}}
>
<FullCalendar
...
/>
</div>
Map classes
From componentDidMount() add whatever logic you need to target the correct DOM node (for your case, I added logic for type and matchingClass). Then run that logic on all FullCalendar DOM nodes and replace the classList on any that match.
componentDidMount() {
this.updatePrimaryBtns();
}
updatePrimaryBtns = () => {
const children = Array.from(this.fullCal.children);
// Options
const type = "BUTTON";
const matchingClass = "fc-button-primary";
this.mapClassToElem(children, type, matchingClass);
};
mapClassToElem = (arr, type, matchingClass) => {
arr.forEach(elem => {
const { tagName, classList } = elem;
// Check for match
if (tagName === type && Array.from(classList).includes(matchingClass)) {
elem.classList = this.primaryBtn.classList.value;
}
// Run on any children
const next = elem.children;
if (next.length > 0) {
this.mapClassToElem(Array.from(next), type, matchingClass);
}
});
};
This is maybe a little heavy handed, but it meets your future proof requirement for when you updated update Material UI. It would also allow you to alter the classList as you pass it to an element, which has obvious benefits.
Caveats
If the 'mapped-to' component (FullCalendar) updated classes on the elements you target (like if it added .is-selected to a current button) or adds new buttons after mounting then you'd have to figure out a way to track the relevant changes and rerun the logic.
I should also mention that (obviously) altering classes might have unintended consequences like a breaking UI and you'll have to figure out how to fix them.
Here's the working sandbox: https://codesandbox.io/s/determined-frog-3loyf

Add CSS property in Angualr2 with MetaWidget

I am trying to add CSS when clicked on row or column of table, Following is code
private rowClicked(event: Event): void {
event.srcElement.setAttribute("class", "highlighted");
}
But it's not working as accepted. Am I doing in wrong way, Is there any alternate way to add CSS dynamically?
Note-
Is there any way to add CSS using dom element, my table has thousands of data and to create this table, I have used MetaWidget.
The easiest way to your problem is to assign a unique ID to each included element together with employing another variable to hold selected ID. The logic to turn on my-class CSS class will now be based on the selected ID.
Your new HTML template:
<div (click)="rowClicked(1);" [ngClass]="{'my-class': highlightedDiv === 1}">
> I'm a div that gets styled on click
</div>
Your rowClicked function:
highlightedDiv: number;
rowClicked(newValue: number) {
if (this.highlightedDiv === newValue) {
this.highlightedDiv = 0;
}
else {
this.highlightedDiv = newValue;
}
}
A working demo is here.
More can be found here.
You are using MetaWidget, but you are not mentioning what version you are using.
If you want work with Angular2 and MetaWidget, you should have use a compatible version of MetaWidget, which can be found here-
https://github.com/AmitsBizruntime/MetawidetA2
Using this library will be the best solution for you.
Re-
Angular does not work based on DOM, it works based on Component.
If you like to work on DOM, then you should include jQuery in tour angular project from here-
How to use jQuery with Angular2?
But it is not a good practice.

Resources