Styling¶
With the basic component and tooling in place, we will now expand that to have a bit more content and then also include some styling. First, copy the contents of the <body>
of the login.html
file from week 1, paste it into the athena.jsx
and adjust the classes to bootstrap, so that it looks like this:
import React from "react";
import ReactDom from "react-dom";
function Athena() {
return (
<div className="container-fluid gx-0">
<header className="row gx-0">
<nav className="navbar navbar-dark bg-dark" aria-label="Main">
<ul className="navbar-nav horizontal" role="menu">
<li className="navbar-brand">Athena - Study Portal</li>
<li className="nav-item">
<a
href=""
role="menuitem"
tabIndex="0"
className="nav-link active"
>
My Modules
</a>
</li>
<li className="nav-item">
<a href="" role="menuitem" tabIndex="-1" className="nav-link">
My Exams
</a>
</li>
</ul>
<ul className="navbar-nav horizontal" role="menu" aria-label="User">
<li className="nav-item">
<a
href="profile.html"
className="nav-link"
role="menuitem"
tabIndex="0"
>
Profile
</a>
</li>
<li className="nav-item">
<a
href="login.html"
className="nav-link"
role="menuitem"
tabIndex="-1"
>
Logout
</a>
</li>
</ul>
</nav>
</header>
<main className="row justify-content-lg-center">
<form className="col-6" action="index.html">
<h1>Login</h1>
<div className="form-group">
<label>
E-Mail Address
<input
className="form-control"
type="email"
name="email"
placeholder="Your e-mail address"
tabIndex="1"
required="required"
/>
</label>
</div>
<div className="form-group">
<label>
Password
<input
className="form-control"
type="password"
name="password"
placeholder="Your password"
tabIndex="2"
required="required"
/>
</label>
</div>
<div className="btn-container">
<button className="button" tabIndex="3">Login</button>
</div>
</form>
</main>
<footer>
© 2018 - Mark Hall (
<a href="mailto:mark.hall@informatik.uni-halle.de" tabIndex="0">
mark.hall@informatik.uni-halle.de
</a>
)
</footer>
</div>
);
}
ReactDom.render(<Athena />, document.getElementById("app-entry-point"));
There are a few rules in JSX that mean that attributes are spelled slightly differently to standard HTML. The first is that instead of class
you must always use className
. The second is that all attributes need to be camel-cased, so for example tabindex
becomes tabIndex
. Make those two changes and the code will compile without warnings in the browser and you will see an unstyled login page.
To add styling, we basically take the same approach as for the JSX support. We need to install additional packages for Webpack, so that it knows how to deal with style files:
$ yarn add sass-loader node-sass style-loader css-loader bootstrap
Next, update the webpack.conf.js
, adding a module rule to deal with the scss files we use to create our styling:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: path.resolve(__dirname, "src/js/athena.jsx"),
output: {
filename: "[name]-bundle.js",
path: path.resolve(__dirname, "dist"),
libraryTarget: "var",
library: "Athena",
},
mode: "development",
plugins: [
new HtmlWebpackPlugin({
title: "Athena",
template: path.resolve(__dirname, "src/index.html"),
inject: false,
xhtml: true,
}),
],
module: {
rules: [
{
test: /\.m?jsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
plugins: ["babel-plugin-transform-class-properties"],
},
},
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "sass-loader",
options: {
sassOptions: {
includePaths: [path.resolve(__dirname, "node_modules")],
},
},
},
],
},
],
},
resolve: {
extensions: ["*", ".js", ".jsx"],
},
devServer: {
static: {
directory: path.resolve(__dirname, "dist"),
},
open: false,
historyApiFallback: true,
},
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
commons: {
test: /node_modules/,
name: "athena-vendor",
chunks: "initial",
minSize: 1,
},
},
},
},
};
Next, create a new directory src/styles
and copy the app.scss``from ``week09/app/styles
into that directory. The most important part of the addition is the includePaths
which tells the SASS/SCSS compiler where to look for files included from our style files.
At this point, if you re-start the build server, you will see that the styling is still not being applied. This is because we haven’t told Webpack where to find it and include it. To include styling we use the standard JavaScript import
function in the athena.jsx
:
import React from 'react';
import ReactDOM from 'react-dom';
import '../styles/app.scss';
function Athena() {
...
}
Because we have configured a module to handle *.scss files, it knows what to do, when a SCSS file is imported and you will see that at this point the application is styled correctly.