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.
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.
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.
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.
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.
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.
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”.