Theming CSS

In this article we're going to look at scoping our CSS, as we further separate our sub-sites.

We're going to start where we left off in the last article.
If you need to catch up:

git clone https://github.com/lawwantsin/webpack-course.git
cd webpack-course
git checkout theming-css
npm install

So we're working through our exercise in splitting an app by domain. We want to add a kind of theming to these individual sites. Providing a way to separate the look and feel of an individual blog without affecting the others.

To do that we have to decide where we want our css to live. At least in the case of the themes. We're going to pull in css by folders named after our domains.

In terminal:

cd src/css
mkdir link zelda
touch link/theme.css zelda/theme.css

Now we pull in that theming using dynamic import() and the props.site again. We do so inside a component's render function or, in the case of Article.js, above the return.

In Article.js, let's the work we started in About.js:

import React from "react"
import "../css/About.css"

export default props => {
  const siteConfig = require(`../../data/${props.site}/siteConfig.js`)
  const imagePath = require(`../images/${siteConfig.aboutImage}`)
  const MarkdownData = require(`../../data/${props.site}/post.md`)

  import(`../css/${props.site}/theme.css`) // Add this line.

  return (
    <div>
      <div className="profile">
        <img src={imagePath} />
        <h1>{MarkdownData.title}</h1>
        <div
          className="content"
          dangerouslySetInnerHTML={{ __html: MarkdownData.__content }}
        />
      </div>
    </div>
  )
}

Update the Routes.js so that the Article route looks like the About route:

<Route
  path="/article/:slug"
  render={({ staticContext }) => {
    const site = staticContext
      ? staticContext.site
      : location.hostname.split(".")[0]
    return <UniversalComponent site={site} match={match} page="Article" />
  }}
/>

We're also getting the match prop and spreading it into our Article component. This will let us access the named parameter slug and we'll use that slug to fetch a particular article.

Take the background color out of main.css.

body {
  margin: 0;
  font-family: Helvetica;
}

We want Link's background to be slightly green and zelda to be slightly blue.

In css/link/theme.css:

body {
  background-color: #2c3227;
}

In css/zelda/theme.css:

body {
  background-color: #020f17;
}

Now when we look at our build, we see the themes are output as separate chunks. They have an additional -css but that's not a deal-breaker for me.

Now, there are a couple of ways to get this into our final render. In render.js at the bottom under the ${js} line, let's insert our new stylesheet:

  ${js}
  <link href="/css/${site}-theme-css.css" rel="stylesheet">
</body>

Theming CSS Example

What about FlushChunks tho?

This hardcoded link tag is a little lazy. FlushChunkNames is really just an array of our chunk's. If we set a debugger, we can see that. So, since we have the site information. Let's use concat to add this extra chunk, and let flushChunks handle the css output for us.

In render.js:

const site = req.hostname.split(".")[0]
const context = { site }
const names = flushChunkNames().concat([`css/${site}-theme-css`])

const { js, styles } = flushChunks(clientStats, {
  chunkNames: names
})

Now we get it in our styles. And it's output in the html, after the rest of the css, so it properly cascades and overrides any other settings. We'll use that in the next article.

Finished

In Sum

In this article we hooked a new stylesheet into the mix for theming purposes. To show how we can use the import function.

I notice the hot reloading dies when we use the import function. We have to reload to see changes. Can't have everything when you're living on the edge.

git checkout theming-css-final

Up Next

Next we're going to build out the Article pages for our heroes a bit, so we have 2 full working examples.