How can I configure create-react-app to export CSS into two different files - sass

I'm working a project that will dynamically allow the user to change themes, and uses reactstrap and styled-components under the hood. We want to configure all of the variables via SASS, which is working fine. In order to make those variables available to styled-components, we have been using sass-extract-loader to create theme objects.
This all works great when we statically choose one of the themes, but I haven't been able to get it working dynamically reliably. I have two issues:
1) In development, it works fine to switch the theme once. If I change it again, my non-styled-components (i.e., raw reactstrap components) are styled with the second theme. I believe this is because the second theme is loading and overriding the original CSS.
2) In production, I get the same mix as #1 by default (i.e., because all of the CSS files are being put together into a single bundle, reactstrap components are styled one way, while styled-components "honors" the theme).
I believe the best option for us is to have the themes in two separate CSS files, and to toggle "alternate" rels on them. I just don't know how to configure CRA not to put all of the CSS into a single main bundle, and let me manually add links to alternate stylesheets. If I can split them out into separate files, I believe I can just add tags and dynamically swap the rel="alternate" property.
There may well be better ways to accomplish this. My understanding is that the easiest way to control the Bootstrap themes is via SASS variables, and I'd like to make sure those variables don't have to be re-defined when using them in styled-components.

If you want to apply styles conditionally, you can import your stylesheets in your index.js file and make it available to all your components through the context API. First of all we import the CSS files into index.js.
import themeA from './css/themeA.css';
import themeB from './css/themeB.css';
However, by using it this way, you cannot have element selectors in both CSS files, because they would be globally applied from both files. However, you could import an extra stylesheet that complements the selected theme, in which you define the element selectors.
By using CSS modules, you avoid the need for element selectors. You may want to read this article if you are unfamiliar with CSS modules: https://javascriptplayground.com/css-modules-webpack-react/
If you still need to apply element selectors in one theme, you can do so, but they will also get applied in your other theme this way.
import './css/default.css';
This example below is a modified version from the React documentation: https://reactjs.org/docs/context.html
In this example, we draw a button that changes the global theme when
it its clicked. There are three parts in this example that are crucial
to understand if you want to use React its context API.
1. Creating a Context.
Creating a Context is being done by assigning the return value of React.createContext(yourValue) to a variable. This variable can then be used to use the Provider or Consumer component inside your React components.
const ThemeContext = React.createContext();
2. Providing a value by passing it to your Provider component as prop. Now your value is accessible to all your components.
class App extends React.Component {
swapTheme() {
this.setState({ withThemeA: !this.state.withThemeA });
}
render() {
const theme = this.state.withThemeA ? themeA : themeB;
return (
<ThemeContext.Provider value={theme}>
<ThemedButton onClick={this.swapTheme} />
</ThemeContext.Provider>
);
}
}
3. Listening to updates with the Consumer component.
To listen for changes, you need to use the Consumer component. The passed function receives the theme as an argument so it can be assigned to the className prop of the button element.
class ThemedButton extends React.Component {
render() {
return <ThemeContext.Consumer>
{theme => <button className={theme.Button}}>click me</button>}
</ThemeContext.Consumer>;
}
}

Related

Change CSS on AJAX request in Wicket

I have a component A that should dynamically change the font size of some of it's contents. I currently use CSS variables to do that and the component will contribute some CSS String containing these CSS variables:
public void renderHead(IHeaderResponse response) {
String fontCss = // dynamically fetch CSS cariables
response.render(CssHeaderItem.forCSS(fontCss, "font-css"));
}
On the same page I have the possibility to change these font sizes using an AJAX update within another component B. This will add the component A to the AjaxRequestTarget, which will cause the renderHead method to be executed with updated values for the font CSS variables.
However, I don't see an updated font size in my browser as the old CSS variables still seem to be present. How can I enforce the new CSS to overwrite the old one?
So far I found 2 solutions, that seem like dirty workarounds to me:
Add the whole page to the AjaxRequestTarget, so the whole page will be refreshed.
Add JavaScript to the AJAX update to remove the old styling with:
var allStyles = document.getElementsByTagName("style");
for (var style of allStyles) {
if (style.getAttribute("id").includes("font-css")) {
style.remove();
}
}
Is there a cleaner solution to this problem?
You found the problem with workaround 2.
response.render(CssHeaderItem.forCSS(fontCss, "font-css"));
adds <style id="font-css"> ... </style> to the page. Later when the Ajax response contributes the new content the JavaScript logic finds that there is an HTML element with id font-css and assumes that there is nothing to do.
A simple solution is to use dynamic id, e.g. by using UUID in #renderHead().
Another solution is to make this <style> a proper Wicket Component, a Label, that could be added to the AjaxRequestTarget with an updated Model when needed.

How to create custom components for Rechart components

Lets say I am creating a bar chart using Recharts, how would i create a custom component for each of the following Recharts components:
XAxis, YAxis, Tooltip, Legend, CartesianGrid, Cell, and Bar
The reason for this s because i am planning to create a chart with a lot of props and wish to separate all the default props and customization in their own individual component for the list component above.
I have tried just putting the CartesianGrid in a react component and the grid will not show
Any ideas?
It seems that you want to wrap the existing Recharts provided components in a custom component for better organization of your code.
This is currently not supported directly by Recharts, as they check for the type of element that you are rendering and if it does not match one of the allowed types it does not get rendered.
For ex:
<LineChart>
<Line></Line>
</LineChart>
would display a line correctly,
But
function MyLine(props) {
return <Line></Line>
}
<LineChart>
<MyLine />
<LineChart>
would not render the line.
This is because, recharts figures out that the MyLine component is not allowed and hence would not be displayed.
This is a big problem as it does not allow us to reuse or compose components.
But there are some workarounds, one of them being calling your custom component as a function directly:
<LineChart>
{
MyLine({})
}
</LineChart>
It also seems like there are no plans to provide such an api in future. All such issues on their github are already closed, without providing a solution.
https://github.com/recharts/recharts/issues/412
https://github.com/recharts/recharts/issues/41
https://github.com/recharts/recharts/issues/1470

programmatically accessing compiled CSS class names in angular 2

In d3, I make a lot of elements like this:
this.title = svg.append('text')
.attr('class', 'graph-title')
.text('blah blah blah');
I was hoping I could put that class in the CSS local to the component creating this svg, but you can't refer to the local css this way. What I did above would only work if I put the relevant style into the global CSS, which for reusability reasons is not what I'd like to do.
Is there some way I can programmatically add the local css class to this dynamically-created element?
You can use >>> (or /deep/) to address dynamically added elements that don't get the encspsulation attributes (_ng_content...) added from styles added to components:
:host >>> graph-title {
...
}

Specifying allowedContent for widgets in CKEditor

I'm implementing a new widget that will be very similar to the simplebox example, so I'm trying to make that work first.
In the tutorial, it is said that specifying allowedContent is enough to include additional elements, classes, styles that we need, so that it is not filtered out. In the main editor configuration I have
div(simplebox);
This successfully enables the widget but still, if I switch to source and back to wysiwyg, things inside like the class for the inner div - "simplebox-content" is deleted and the widget no longer works properly. I haven't changed the code for simplebox.
Am I doing something wrong here?
Take a look on widget definition in Simplebox plugin:
editor.widgets.add( 'simplebox', {
// Allow all HTML elements, classes, and styles that this widget requires.
// Read more about the Advanced Content Filter here:
// * http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter
// * http://docs.ckeditor.com/#!/guide/plugin_sdk_integration_with_acf
allowedContent:
'div(!simplebox,align-left,align-right,align-center){width};' +
'div(!simplebox-content); h2(!simplebox-title)',
// Minimum HTML which is required by this widget to work.
requiredContent: 'div(simplebox)',
...
} );
It says that the minimal requiredContent for the widget to work is div(simplebox). By "minimal", it means that it will run but with a limited set of features.
And this is exactly what happens when you set config.allowedContent = 'div(simplebox)'. If you compare it with allowedContent property in widget definition, you'll notice that by default, the widget registers much more than that in editor's filter, like .align-left, .align-right, .align-center and .simplebox-content classes for <div> and .simplebox-title class for <h2>. Once you set config.allowedContent, you override the rules defined by the widget and if some elements/classes become disallowed because of that, the structure of the widget gets "flattened", just like described
[...] things inside like the class for the inner div - "simplebox-content" is deleted and the widget no longer works properly
So the long story short: When you define config.allowedContent on your own, you are responsible for the shape of editor features and you must understand the consequences. Only if you include all the rules from the definition of Simplebox, you'll enjoy the full functionality of the widget.
I had the same problem. What I did was I added the classes inside editables variable like this:
editables: {
content: {
selector: '.static-gray-box',
allowedContent: 'h5(first-child,last-child);h6(first-child,last-child);p(first-child,last-child)'
}
},

Set homepage from theme's layout

I am trying to set a CMS homepage via a theme's local.xml layout update file in the <cms_index_index> node. I swear I've seen functions to change the store configuration temporarily within a layout node (but maybe I dreamt it), but I'm having trouble finding the layout function in classes like Mage_Core_Block_Abstract and its children classes.
For reference, I've checked in Mage_Cms_IndexController and found the function which renders the homepage:
public function indexAction($coreRoute = null)
{
$pageId = Mage::getStoreConfig(Mage_Cms_Helper_Page::XML_PATH_HOME_PAGE);
if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
$this->_forward('defaultIndex');
}
}
Or, am I doing this completely the wrong way? What would be best practice for a problem like this? I do not want to add a store view for the new theme, as the new theme is for mobile platforms and requires the same settings from the store view. Thanks guys!
This is not possible. The layout configuration is not invoked until after checks occur to see if there is a valid page which has been specified; because these checks fail, the Default router will match and (by default) the application will display the 404 page.

Resources