Be Responsive

Related Blogs
Shambhu

Front-end Developer

9 min read

As a web developer or designer, it's crucial to ensure that your website looks and functions properly across all devices. With the rise of mobile browsing, it's more important than ever to ensure your website is responsive and adapts to different screen sizes. However, testing your website on multiple devices can take time and effort.

Introducing “Be Responsive”, a new chrome extension that can help you to build your website much faster by showing your current web page with two different layout's 75% for desktop view and the rest 25% for mobile devices. One of the main advantages of using Be Responsive is that it allows you to easily compare and contrast the different layouts and designs of a website across different devices. This can be particularly useful for identifying and fixing issues with responsive design, such as elements that are not properly scaling or aligning on mobile devices.

Advantages:

  • Improved responsive design testing: By being able to view both the desktop and mobile versions of a website side by side, developers and designers can easily identify and fix any issues with responsive design, such as elements that are not properly scaling or aligning on mobile devices.
  • Improved website functionality testing: quickly and easily test how different features and interactions behave on different devices and identify and fix any issues that may arise.
  • Time-saving: view websites on multiple devices without having to physically access them, saving time and effort.
  • Easy comparison: easily compare and contrast the different layouts and designs of a website across different devices.
  • Convenience: view the website on the current window of chrome, without the need of switching between different devices or opening multiple tabs.
  • Real-time testing: view websites in real-time, as they are working on it, making it easier to identify and fix issues as they arise.

Challenges Behind the Creation of Be Responsive

Be responsive is created like any other extension that has content script, background script, and popup.html but it uses create-react-app and the main issue using CRA in the chrome extension is that it's built using webpack, which is configured to bundle our application’s code and generate chunks of JavaScript which are then loaded by the application, This method of chunking code results in randomly generated files whose names change each time the build command is executed.
It is important to recall that in the context of chrome extensions, there are content and background scripts as well as a manifest.json file. In the manifest, the paths to the scripts are specified to inform the browser which files it should load. However, the manifest is a static JSON file, which poses a problem in that it does not provide a means of dynamically identifying the names of the JavaScript chunks that are intended to be loaded. To overcome this issue, it is necessary to reconfigure webpack in such a way that it generates JavaScript files with unchanging names. This can be achieved by ejecting from the Create React App.

npm run eject

This command will create a /config directory and there expose webpack configuration files to us.
We need to change two files, ‘paths.js’ and ‘webpack.config.js’ to disable chunking and tell webpack what to name the files.

Copy Code
            
  module.exports = {
  dotenv: resolveApp('.env'),
  appPath: resolveApp('.'),
  appBuild: resolveApp(buildPath),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/popup.html'), // add
  appIndexJs: resolveModule(resolveApp, 'src/popup/index'), // add
  appBackgroundJs: resolveModule(resolveApp, 'src/background/index'), // add
  appContentJs: resolveModule(resolveApp, 'src/contentScript/index'), // add
            
          

Add popup, background index and content index Js files – paths.js Inside the path.js file we have to specify our files paths for contentScript, background, and index.js file so that we can then use it inside the webpack.config.js file.

Copy Code
            
    entry: {
        popup:
            isEnvDevelopment && !shouldUseReactRefresh
            ? [
                webpackDevClientEntry,
                paths.appIndexJs,
                ]
            : paths.appIndexJs, 
        background: paths.appBackgroundJs,
        content: paths.appContentJs,
    },
            
          

Define popup and content, background entry points – webpack.config.js

After defining the file paths, we must specify the entry points in the webpack.config.js file. The "entry" property specifies the application's starting point and tells webpack which file(s) to build the application. It's important to include entry paths for the popup, background, and content files in order for webpack to bundle the application from these entry points properly.

Copy Code
            
    output: {
      path: isEnvProduction ? paths.appBuild : undefined,
      pathinfo: isEn Development,
      filename: isEnvProduction
        ? 'static/js/[name].js' // remove [contenthash]
        : isEn Development && 'static/js/bundle.js',
      futureEmitAssets: true,
      chunkFilename: isEnvProduction
        ? 'static/js/[name].js' // remove [contenthash]
        : isEn Development && 'static/js/[name].chunk.js',
            
          

Define output, remove the [contenthash] part from the generated file names - webpack.config.js

Now that our paths and entry points have been configured, it is necessary to remove the [contenthash] property from the output property in the webpack.config.js file to ensure that the file names generated by webpack match those specified in the manifest.json file after building the application.

Copy Code
 

plugins: [

new HtmlWebpackPlugin( Object.assign( {}, { inject: true, template: paths.appHtml, filename: 'popup.html', }, isEnvProduction ? { excludeChunks: [ 'background', 'content', ],

Pass popup.html as name and exclude content from being injected into popup.html webpack.config.js

Great! So far, we have set up our file paths, entry points, and naming of our files. Now, we can move on to configuring the html-webpack-plugin. This plugin will be used to create a pop-up window for our extension by specifying the template and file name for our 'popup.html'. To ensure that the 'background' and 'content' chunks are excluded from the generated HTML file, but still included in the webpack bundle, we can add them to the excludeChunks option.

Copy Code

    new ManifestPlugin({
    fileName: 'asset-manifest.json',
    publicPath: paths.publicUrlOrPath,
    generate: (seed, files, entrypoints) => {
        const manifestFiles = files.reduce((manifest, file) => {
        manifest[file.name] = file.path;
        return manifest;
        }, seed);
        const entrypointFiles = entry points.popup.filter( // change main to popup
        fileName => !fileName.endsWith('.map')
        );




            
          

Rename main into popup- webpack.config.js

Lastly, we need to change the main entrypoint to 'popup' entrypoint, as that is what we have named it. This will allow us to control the output of our JavaScript and maintain static file paths.
We have taken care of everything, but we still need to inject our content script into the visited page so that it can perform its functions and display our webpage with two different layouts. To isolate our script and styles, we have wrapped it in a web component. In the index file, we have packaged the app into a web component and mounted it as a direct child of the webpage's HTML document, instead of mounting it on the "root" div. To achieve this, we have used the @webcomponents/custom-elements package.

Copy Code

    import React from "react";
    import ReactDOM from "react-dom";
    import "@webcomponents/custom-elements";
    import ContentScript from "./ContentScript";
    import { StylesProvider, jssPreset } from "@material-ui/styles";
    import { create } from "jss";
    
    class ReactExtensionContainer extends HTMLElement {
    connectedCallback() {
        const mountPoint = document.createElement("span");
        mountPoint.id = "reactExtensionPoint";
    
        const reactRoot = this.attachShadow({ mode: "open" }).appendChild(
        mountPoint
        );
    
        const jss = create({
        ...jssPreset(),
        insertionPoint: reactRoot,
        });
    
        ReactDOM.render(
        
            
        ,
        mountPoint
        );
    }
    }
    
    const initWebComponent = function () {
    customElements.define("react-extension-container", ReactExtensionContainer);
    
    const app = document.createElement("react-extension-container");
    document.documentElement.appendChild(app);
    };





            
          

src/contentScript/index.js

We have also used MaterialUI and JSS for the modal component as they both work well with the web component shadow DOM and require minimal additional configuration.

Limitation:

Be Responsive may have limitations such as not being able to fully emulate the mobile device, not being able to accurately display all web pages, security restrictions, limitations on simultaneous connections, and not being able to display pages with the "X-Frame-Options" header set to "DENY" or "SAMEORIGIN".

Github Repo

Back To Blogs


Find out our capabilities to match your requirements

contact us