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

Tips for deciding on the appropriate CSS for an Angular 6 navbar component

I am currently working on an angular 6 application where users are assigned different roles that require distinct styling. Role 1 uses Stylesheet 1, while Role 2 uses Stylesheet 2. The Navbar component is a crucial part of the overall layout structure of ...

Can using .wrap( ) negate the styling effects of the wrapper?

I am struggling with centering a button on my webpage. Currently, I have the following code for creating the button: var button = ($('<button>', { "id": "jspsych-free-sort-done-btn", "class": "jspsych-free-sort", ...

Troubleshooting npm problem: conflict between react 17.0.2 and "react-swipeable-views" version "0.13.9"

I have been attempting to set up the material dashboard by cloning the repository using the following command: git clone https://github.com/creativetimofficial/material-dashboard-react.git. I am using node version 16 for this setup. After running npm inst ...

Mastering the art of square bracket destructuring in React through deep comprehension of the concept

import React, { useEffect, useState } from 'react' import { Text } from 'react-native' export default function Counter() { const [count, setCount] = useState(0) useEffect(() => { const id = setInterval(() => setCount((co ...

Can we send a file using res.sendFile() and then redirect using res.redirect() simultaneously in an Express ReactJS application?

I've been working on a ReactJS/ExpressJS application with SSR implemented. I've run into an issue where I need to redirect every route of my website to www. Currently, I am serving static files and also want to implement redirects for all routes. ...

Unable to access a hyperlink, the URL simply disregards any parameters

When I click an a tag in React, it doesn't take me to the specified href. Instead, it removes all parameters in the URL after the "?". For example, if I'm on http://localhost:6006/iframe.html?selectedKind=Survey&selectedStory=...etc, clicking ...

Tips for repairing damaged HTML in React employ are:- Identify the issues

I've encountered a situation where I have HTML stored as a string. After subsetting the code, I end up with something like this: <div>loremlalal..<p>dsdM</p> - that's all How can I efficiently parse this HTML to get the correct ...

Updating an item in an array stored in state using user input in a React.js application

Currently, I am working on a react madlibs app and facing an issue with updating the this.state.blanks array accurately as the user inputs words. I believe there might be an issue with binding (even though I used a fat arrow function which should bind it) ...

Exploring the ins and outs of webpage loading speed

I am working on writing JavaScript code that includes a button to open a webpage of my choice. I now want to understand how to detect when the page I called is finished loading. Any suggestions or ideas on this topic? I apologize if my explanation was no ...

Can you explain the mechanics behind the animation of the upvote button on steemit.com?

Behold the upvote button of steemit.com: <span class="Icon chevron-up-circle" style="display: inline-block; width: 1.12rem; height: 1.12rem;"> <svg enable-background="new 0 0 33 33" version="1.1" viewBox="0 0 33 33" xml:space="preserve" xmlns=" ...

Expand the range of input movement using a unique and oversized custom thumb graphic

In my project, I am creating a personalized input range feature by utilizing a 40px x 80px image as the background for the thumb. Typically, the thumb is limited to moving within the length of the track from edge to edge. However, I am aiming for the thu ...

Is it possible to implement dependency injection within a .css document?

I have a C# .NET 6 application. Some of the web pages (Razor Pages) in the app use dependency injection to inject configuration into the Razor Pages (.cshtml files), allowing certain config elements to be displayed in the user interface. My query is, can ...

What is the material-ui component that mirrors the functionality of <input type="color"><datalist>?

I'm just starting out with javascript, react, and Material-UI, so my question (along with the code sample) might show my lack of experience. Within a Material-UI TableCell (not a form), I have the following code: <input type="color" name ...

Adapting padding based on the height of the browser to ensure optimal layout

I'm interested in adjusting the padding value of a button element based on the height of the browser window, not its width. I want to make sure that the padding adjusts proportionally to the browser window's height. Is this achievable using CSS? ...

What is the best way to showcase both successful and failed API requests in a Next.js application?

I input the names of characters separated by commas, like Ricky, Marty, etc. Then, I send requests to a database for each character and display the results. How can I show a list of successful requests along with unsuccessful requests if a hero is not fo ...

The Crimson Thread when incorporating tsx into Next.js

While working with TSX in React and TypeScript, I encountered an issue. A red line appeared on the screen even though the project runs successfully. Can anyone explain why this red line is appearing and why the classes in TSX are not functioning properly ...

Incorrect formatting of HTML emails in Outlook

I crafted an html e-mail using the code below: <!DOCTYPE html> <html style="margin:0px;padding:0px;"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> </head> <body style="mar ...

Using ReactJs to showcase various Child components nested within a Parent component (Preference)

My goal is to accomplish something similar to this: https://codesandbox.io/s/3vqyo8xlx5 However, I would like the child element to be displayed underneath the location where the "Add Child" button is clicked, rather than at the end. I kindly ask for ass ...

Creating a stylish CSS button with split colors that run horizontally

Could you please provide some guidance on creating a button design similar to this one? I've made progress with the code shown below, but still need to make adjustments like changing the font. <!DOCTYPE html> <html> <head> <sty ...

Error: Unable to access 'length' property of null in Next.js issue

I am encountering an error in my Next.js files: 117 | }; 118 | > 119 | if (data.length <= 0) { | ^ 120 | return null; 121 | } 122 | If I want to display it with an image, the error looks like this:https://i.stack ...