Create a right-to-left (RTL) CSS file within a create-react-app project and dynamically switch between them depending on changes

I am currently working on a multi-language project using create-react-app. My goal is to incorporate a library like "cssJanus" or "rtlcss" to transform the Sass generated CSS file into a separate file. This way, I can utilize the newly created file when switching between languages.

This is an overview of my index.js file:

import React from "react";
import ReactDOM from "react-dom";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
import { Provider } from "react-redux";
import App from "./App";
import { configureStore } from "./store/configureStore";

const store = configureStore();

ReactDOM.render(
    <Provider store={store}>
        <Router>
            <App />
        </Router>
    </Provider>,
    document.getElementById("root")
);

serviceWorker.unregister();

Below is what my "App.js" file looks like:

import React, { Component } from "react";
import "./App.scss";
import { Route, Switch } from "react-router-dom";
import SignIn from "./features/signin/SignIn";

class App extends Component {
    render() {
        return (
            <>
                <Switch>
                    <Route path="/" exact component={SignIn} />
                </Switch>
            </>
        );
    }
}

export default App;

In my current setup, I am using the "./App.scss" file which contains several @import statements pointing to other ".scss" files within the "./src/css/" directory:

/* autoprefixer grid: on */
@import "css/reset";
@import "css/variables";
@import "css/global";

I would greatly appreciate your advice on how to achieve this transformation process. Specifically, I need guidance on converting the CSS generated from App.scss to RTL and saving them into their respective .css files. Furthermore, I aim to toggle between these generated CSS files based on a change in the global state.

Despite thorough research efforts, I have been unable to find a suitable solution for this task. If you have any alternative suggestions or better approaches, I am open to hearing them.

Answer №1

Here is an easy fix that involves ejecting and integrating a lightweight webpack-rtl-plugin.

After following these steps:

npx create-react-app react-rtl 
cd react-rtl
yarn eject
yarn add -D webpack-rtl-plugin @babel/plugin-transform-react-jsx-source

Open up the config/webpack.config.js file and make some adjustments:

// include the plugin
const WebpackRTLPlugin = require('webpack-rtl-plugin')

// ...

module: { ... }
plugins: [
   // ...,
   // implement the plugin
   new WebpackRTLPlugin({ diffOnly: true })
].filter(Boolean),
// ...

Upon completion, when you run yarn build and navigate to the build/static/css directory, you should see an additional .rtl.css file containing your rtl styles. Next, configure webpack to utilize MiniCssExtractPlugin.loader for development purposes as well to render styles via link tags instead of inline styles:

// function to obtain style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [
    isEnvDevelopment && { loader: MiniCssExtractPlugin.loader }, // <-- use this
    // isEnvDevelopment && require.resolve('style-loader'), <-- replace with this 

Don't overlook adding the plugin:

module: { ... }
plugins: [
   // ...,

   // isEnvProduction &&      <-- comment out
   new MiniCssExtractPlugin({
     // Similar options as in webpackOptions.output
     // both are optional
     filename: 'static/css/[name].[contenthash:8].css',
     chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
   }),

   // ...
].filter(Boolean),

Finally, grab the default stylesheet's href and incorporate it to insert rtl styles. Here's a sample implementation:

class RtlCssBundleService {
  constructor() {
    this.rtlApplied = false
    this.rtlStyles = [];
    this.ltrStyles = Array.from(
      document.querySelectorAll('link[rel="stylesheet"]')
    )
  }

  insert = () => {
    if (this.rtlApplied) { return }

    this.rtlApplied = true

    if (this.rtlStyles.length) {
      return this.rtlStyles.forEach(style => {
        document.body.appendChild(style)
      })
    }

    this.rtlStyles = this.ltrStyles.map(styleSheet => {
      const link = document.createElement("link")
      link.href = styleSheet.href.replace(/\.css$/, '.rtl.css')
      link.rel = "stylesheet"
      document.body.appendChild(link)
      return link
    })
  }

  detach = () => {
    this.rtlApplied = false
    this.rtlStyles.forEach(style => {
      document.body.removeChild(style)
    })
  }

  toggle = () => {
    return this.rtlApplied
      ? this.detach()
      : this.insert()
  }
}

const rtlStyles = new RtlCssBundleService()

export default rtlStyles

You can now use this in any component. It may not be perfect, but it functions. Check out the demo for more information.

Answer №2

Utilize the built-in RTL support of flexbox and CSS grid, and enhance it further with CSS Logical Properties for margin, padding, border, etc. If necessary, have a fallback option using [dir="rtl"] .your-class.

No need to juggle two separate CSS files anymore.

Check out this cross-browser example of margin-right:

-webkit-margin-end: 25px;
margin-inline-end: 25px;
@supports (not (-webkit-margin-end: 0)) and (not (margin-inline-end: 0)) {
    margin-right: 25px;
}

You can even create a mixin incorporating these styles for seamless integration throughout your application.

Answer №3

Have you checked out the library called react-with-direction by airbnb? It offers a DirectionProvider component that allows you to adjust your components based on language settings. Give it a try!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If the height of the window changes, then update the CSS height accordingly

Here is the code that I am using: $(document).ready(function() { $('body').css('overflow', 'scroll'); var heightWithScrollBars = $(window).height(); $('body').css('overflow', 'auto') ...

display a visual element within a function using reasoning

I am trying to display an image based on a specific condition in my code, for example: if(x==1) { showImage }. However, I am facing an issue where the web content is not displayed until the image is fully loaded. What I want is for the image to appear smoo ...

What could be causing my cross fade to not repeat as intended?

I created a basic background image cross fader using the code found at http://jsfiddle.net/jRDkm/2/. This code was inspired by . However, I'm encountering an issue where the slideshow only repeats once before fading to white. How can I modify the cod ...

What is the best way to integrate tailwind (tw.macro) when passing a function as a prop in next.js?

Sidebar.tsx import React from 'react'; import { Props } from './SidebarRow.i'; import tw from 'twin.macro'; function SidebarRow({ Icon, title }: Props) { return ( <div> <Icon tw={'h-6 w-6'} /> ...

Add items to a separate array only if the material UI checkbox is selected

Exploring the world of React, I decided to create a simple todo app using React JS and Material UI. With separate components for user input (TodoInput.js) and rendering individual todos with checkboxes (TodoCards.js), I aim to display the total number of c ...

Exploring the combination of React Native, Redux, and React Navigation for

Is it still possible to utilize Redux for managing the state of my app even though React Navigation no longer supports it? I am new to Redux and struggling to find information on this topic. ...

Determine the number of rows in the Tabulator's table

Can anyone tell me how to retrieve the number of rows in a table created using Tabulator on a website? Is there a method like table.size or table.length that I can use for this purpose? The table has been initialized with the following code: table = new T ...

Tips for creating a scrollable section within a div row using clarity design instead of bootstrap formatting

My current project combines Angular with .NET Core, and I am encountering difficulties in styling one particular component. This component consists of a row with two columns. The first column is empty while the second column contains another row divided i ...

"ReactJS is not displaying the sorted list in ascending or descending order when sorting by Asc/Desc in

Development Project Overview Exploring the world of ReactJS, I am immersed in building a Pokedex application that empowers users to categorize Pokemon entities based on specific attributes such as name, type, health points, etc. The ultimate vision is to ...

Is there a way to exclusively utilize CSS in order to achieve bottom alignment for this btn-group?

I currently have a series of div elements that I transformed into two columns. https://i.stack.imgur.com/xG7zT.png My goal is to align the PDF/XML button group at the bottom, creating a layout like this: https://i.stack.imgur.com/ijtuH.png This is an e ...

Enhancing ReactJS functionality by incorporating custom logic prior to resolving promises

In one of my components, there is a function as follows: this.props.firebase.getDropSites("123456").then(data => { console.log(data); }); This function in turn calls the following method from my utilities class: getDropSites(dropSiteId) { return th ...

When you try to create a popover, a cautionary message pops up indicating that $tooltip is no longer supported. It is

I need help with creating a popover that appears when hovering over an anchor tag. Here is the code I am using: angular: 1.4.2 ui-bootstrap :0.14.2 <div class="row" ng-repeat="endorsement in endorsements| filter: {category:categorySelected}"> &l ...

Modify CSS image according to the user interface language in asp.net

Is there a way to dynamically change the image based on different cultures in my ASP.NET webpage? I have successfully been able to switch strings using a resource file, but I am unsure how to handle images. Currently, I have an A tag with a specific clas ...

Move the text from one text editor to another on a different page

Request: I am looking for a way to transfer content from TextEditor1 to TextEditor2, which are located on separate pages in a React Router Web App. The user inputs data into TextEditor1 on Page1 and upon submission, I want the content to be displayed in T ...

Unable to use the select function in Android 2.2

When I click on the select button in my app, it displays multiple options. This functionality works smoothly on Android 4.1, but on Android 2.2, clicking on the select button does not have any effect and therefore the options cannot be selected. <sec ...

Arranging cards in a .map by stacking them on top of each other instead of placing them

I've been struggling to make the cards on my portfolio page display side by side on larger screens and then stack at a specific breakpoint. I'm using Material UI, but no matter how many times I tweak the code, it just won't work as intended. ...

Guide to comparing the contents of two text fields and highlighting the altered characters using ReactJS

Is there a way to compare the contents of two Material-UI textfields and identify the characters that have changed in both? I came across a similar question, but it was specifically for ReactJS rather than C# Windows Forms: How can you highlight the chara ...

Troubleshooting the Timepicker import issue in ant design version 5.0.3

After updating ant design to version 5.0.3, I encountered the Uncaught Error: Cannot find module 'antd/lib/time-picker/style' at webpackMissingModule issue. Any suggestions on how to resolve this? I am seeking a solution for the error coming fro ...

getting a state value from a React class component

Within my App.js file, I have a state object that looks like this: App.js this.state = { headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", Authorization: "Bearer eyJ0eXAiOiJKV1QiL ...

Leveraging nginx for serving dynamic content

I recently utilized nginx to host my node.js application. After creating a build of the app, I configured the root directory in my nginx.conf file to point to the location of the build folder. It was successful, and my application ran smoothly on nginx. H ...