Proper Way To Add Fonts To Sage 9 Theme

I have spent hours trying to get a Google font to work in my Sage + Bootstrap starter theme and read multiple posts on this forum(including one similarly titled from 2017 but I am still at a loss. Hoping someone can help me.

I did the following to use Roboto in my theme:

  1. Downloaded different Roboto weights and styles and dropped those into the Fonts folder.
  2. Used https://google-webfonts-helper.herokuapp.com/ to select the font family.
  3. Copied the following and pasted into a new .scss file under Assets > Styles > Common next to _global.scss and _variables.scss
  4. Ran “Yarn Build”
  5. Tried to import that stylesheet in main.scss

I just get errors that say the relative modules could not be found.

No fonts are compiled into the Dist folder, nor does a new font folder even get created there. I can’t get past the above errors. Super frustrating. Any ideas?

Hey @James_Lanman ,

Where do you copy font files?

It is also recommended to add special prefetching for the Google fonts to improve performance:

Hi Dan. I copied the font files into:

Resources > Assets > Fonts [here]

I wanted to post the error log but I’m only allowed a certain number of links as a new poster so all of the localhost links in the error log count against that.

I expected ‘Yarn Build’ to at least create a fonts folder under dist but as that isn’t happening I feel something is amiss? Maybe with webpack.

Hey Strarsis. Thank you. Are you saying I shouldn’t go the ‘local link’ route to files I’m using and instead combine wp_resource_hints with a link to the google hosted version of these fonts? Is there a singular recommended way to do this? Sorry, I’m new to Sage but I feel like I’m going a little crazy executing this super simple step and it still doesn’t seem to be working.

Locally hosted fonts can also improve performance of course. It depends.
So you planned to host the Google font files yourself? This is easy as all browsers support WOFF/WOFF2 now.

1 Like

I was planning on hosting them myself. But as someone who has learned web development the autodidact route I always have a sneaking suspicion that I’m not doing something “best practice” so like to get guidance from those who know more. I think a lot of folks out there have holes in the basics for similar reasons. But great to know about prefetch/reload resources in wp for hosting fonts from links.

Btw - I just resolved my issue! I think… Perhaps I missed something but it looks like webpack doesn’t understand local file paths like " …/ " and when I replaced those double dots simply with a tilda “~” it fixed the issue and compiled correctly. Elsewhere in this forum, everyone was advising the relative path for local font files. Like I said, I am far from an expert so maybe this was obvious to others. Dist folder is still not generating a “fonts” folder on ‘yarn build’ however.

laravel-mix which Sage 10 uses and that generates a webpack configuration process URLs by default:
https://laravel.com/docs/8.x/mix#url-processing
However, in Sage 10 this is disabled because of performance reasons and because it isn’t used as often:

You can re-enable it by commenting out that line for example.

It should copy the fonts to public by default:

So even without URL processing enabled you can use the right relative path (relative to the stylesheet) and it should also load properly.

1 Like

That’s good to know!

Any idea why ‘yarn build’ still isn’t creating a ‘fonts’ folder? I just checked and looks like I’m on a 9.0.10 version of Sage and not version 10. Sorry about that. Could have sworn I updated. I’m assuming this is an issue with the version i’m using, right?

If you’re using Sage 9, it doesn’t use mix so the above code isn’t relevant to you.

Sage 9 uses a webpack build process that will only compile assets that are used–i.e. they are referenced somewhere. From your description, you’ve only set up some font-face rules–you never actually reference those fonts in your SCSS, so when webpack runs it determines you aren’t actually using them and doesn’t build them. Try putting a font-family rule in your scss that references the fonts.

Thanks alwaysblank and again, apologies for the version mixup. I am currently referencing font-family in my main.scss file like so:

body { background: transparent; font-family: "Roboto", sans-serif; }

and importing the whole of fonts.scss (which I created under ‘resources’ containing this:

/* roboto-regular - latin */ @font-face { font-family: "Roboto", sans-serif; font-style: normal; font-weight: 400; src: url("~/fonts/roboto-v29-latin-regular.eot"); /* IE9 Compat Modes */ src: local(""), url("~/fonts/roboto-v29-latin-regular.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */ url("~/fonts/roboto-v29-latin-regular.woff2") format("woff2"), /* Super Modern Browsers */ url("~/fonts/roboto-v29-latin-regular.woff") format("woff"), /* Modern Browsers */ url("~/fonts/roboto-v29-latin-regular.ttf") format("truetype"), /* Safari, Android, iOS */ url("~/fonts/roboto-v29-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */ }

Then added @import "common/fonts"; to my main.scss file.

Am I doing this right? Still no fonts in dist.

I’m unclear why you’re doing this:

url("~/fonts/roboto-v29-latin-regular.ttf")

A tilde is going to resolve to either your user’s home directory or node_modules in your current project, neither of which are likely to contain your fonts. IIRC they should us some kind of relative file path, i.e. url('../fonts/roboto-v29-latin-regular.ttf'). I checked a couple old Sage 9 themes, and their font paths are ../fonts/font-name.ttf so that should work. If the tilde is silencing your error, it doesn’t seem to be because that provides a path to the font.

You are totally right. Strangely, the link in head started mysteriously working and displaying ‘Roboto’. At least i have a solution to move forward with a direct link to google fonts in the head. But I still don’t understand why using relative file path does not work and throws errors.

Can you post the text of the errors and your configuration? It’s difficult to debug w/o those things.

Yes - sorry about that. Initially, I couldn’t post many links due to new membership restrictions.

My configuration is:

{
  "name": "sage",
  "version": "9.0.10",
  "author": "Roots <team@roots.io>",
  "homepage": "https://roots.io/sage/",
  "private": true,
  "repository": {
    "type": "git",
    "url": "git://github.com/roots/sage.git"
  },
  "bugs": {
    "url": "https://github.com/roots/sage/issues"
  },
  "licenses": [
    {
      "type": "MIT",
      "url": "http://opensource.org/licenses/MIT"
    }
  ],
  "browserslist": [
    "last 2 versions",
    "android 4",
    "opera 12"
  ],
  "scripts": {
    "build": "webpack --progress --config resources/assets/build/webpack.config.js",
    "build:production": "webpack --env.production --progress --config resources/assets/build/webpack.config.js",
    "build:profile": "webpack --progress --profile --json --config resources/assets/build/webpack.config.js",
    "start": "webpack --hide-modules --watch --config resources/assets/build/webpack.config.js",
    "rmdist": "rimraf dist",
    "lint": "npm run -s lint:scripts && npm run -s lint:styles",
    "lint:scripts": "eslint resources/assets/scripts resources/assets/build",
    "lint:styles": "stylelint \"resources/assets/styles/**/*.{css,sass,scss,sss,less}\"",
    "test": "npm run -s lint"
  },
  "engines": {
    "node": ">= 8.0.0"
  },
  "devDependencies": {
    "autoprefixer": "^10.0.1",
    "browser-sync": "^2.26.13",
    "browsersync-webpack-plugin": "^0.6.0",
    "bs-html-injector": "~3.0",
    "buble-loader": "^0.4.1",
    "cache-loader": "~1.2.5",
    "clean-webpack-plugin": "^0.1.18",
    "copy-globs-webpack-plugin": "^0.2.0",
    "css-loader": "^0.28.11",
    "cssnano": "^4.0.5",
    "eslint": "~4.19.1",
    "eslint-loader": "^4.0.2",
    "eslint-plugin-import": "^2.14.0",
    "extract-text-webpack-plugin": "~3.0.2",
    "file-loader": "^6.2.0",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "imagemin-mozjpeg": "^9.0.0",
    "imagemin-webpack-plugin": "^2.4.2",
    "import-glob": "~1.5",
    "node-sass": "^5.0.0",
    "postcss-loader": "^4.0.4",
    "postcss-safe-parser": "^5.0.2",
    "resolve-url-loader": "^3.1.2",
    "rimraf": "^3.0.2",
    "sass-loader": "~6.0",
    "style-loader": "^0.23.1",
    "stylelint": "^13.7.2",
    "stylelint-config-standard": "^20.0.0",
    "stylelint-webpack-plugin": "^0.10.5",
    "uglifyjs-webpack-plugin": "^1.3.0",
    "url-loader": "^4.1.1",
    "webpack": "~3.10.0",
    "webpack-assets-manifest": "^1.0.0",
    "webpack-dev-middleware": "~2.0.4",
    "webpack-hot-middleware": "^2.22.3",
    "webpack-merge": "~4.1.4",
    "yargs": "^16.1.0"
  },
  "dependencies": {
    "bootstrap": "v4.3.1",
    "jquery": "^3.3.1",
    "popper.js": "^1.14.7"
  }
}

Here is what the error output is when I changed the relative paths back to “…/”

ERROR  Failed to compile with 5 errors                               2:40:27 PM

These relative modules were not found:

* ./fonts/roboto-v29-latin-regular.eot in ./node_modules/cache-loader/dist/cjs.js!./node_modules/css-loader?{"sourceMap":true}!./node_modules/postcss-loader/dist/cjs.js?{"postcssOptions":{"path":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets/build","ctx":{"open":true,"copy":"images/**/*","proxyUrl":"http://localhost:3000","cacheBusting":"[name]_[hash:8]","paths":{"root":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe","assets":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets","dist":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist"},"enabled":{"sourceMaps":true,"optimize":false,"cacheBusting":false,"watcher":true},"watch":["app/**/*.php","config/**/*.php","resources/views/**/*.php"],"entry":{"main":["./scripts/main.js","./styles/main.scss"],"customizer":["./scripts/customizer.js"]},"publicPath":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist/","devUrl":"http://localhost:8888/jameslanmandotcom","env":{"production":false,"development":true},"manifest":{}}},"sourceMap":true}!./node_modules/resolve-url-loader?{"sourceMap":true}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true,"sourceComments":true}!./node_modules/import-glob!./resources/assets/styles/main.scss
* ./fonts/roboto-v29-latin-regular.svg in ./node_modules/cache-loader/dist/cjs.js!./node_modules/css-loader?{"sourceMap":true}!./node_modules/postcss-loader/dist/cjs.js?{"postcssOptions":{"path":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets/build","ctx":{"open":true,"copy":"images/**/*","proxyUrl":"http://localhost:3000","cacheBusting":"[name]_[hash:8]","paths":{"root":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe","assets":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets","dist":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist"},"enabled":{"sourceMaps":true,"optimize":false,"cacheBusting":false,"watcher":true},"watch":["app/**/*.php","config/**/*.php","resources/views/**/*.php"],"entry":{"main":["./scripts/main.js","./styles/main.scss"],"customizer":["./scripts/customizer.js"]},"publicPath":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist/","devUrl":"http://localhost:8888/jameslanmandotcom","env":{"production":false,"development":true},"manifest":{}}},"sourceMap":true}!./node_modules/resolve-url-loader?{"sourceMap":true}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true,"sourceComments":true}!./node_modules/import-glob!./resources/assets/styles/main.scss
* ./fonts/roboto-v29-latin-regular.ttf in ./node_modules/cache-loader/dist/cjs.js!./node_modules/css-loader?{"sourceMap":true}!./node_modules/postcss-loader/dist/cjs.js?{"postcssOptions":{"path":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets/build","ctx":{"open":true,"copy":"images/**/*","proxyUrl":"http://localhost:3000","cacheBusting":"[name]_[hash:8]","paths":{"root":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe","assets":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets","dist":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist"},"enabled":{"sourceMaps":true,"optimize":false,"cacheBusting":false,"watcher":true},"watch":["app/**/*.php","config/**/*.php","resources/views/**/*.php"],"entry":{"main":["./scripts/main.js","./styles/main.scss"],"customizer":["./scripts/customizer.js"]},"publicPath":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist/","devUrl":"http://localhost:8888/jameslanmandotcom","env":{"production":false,"development":true},"manifest":{}}},"sourceMap":true}!./node_modules/resolve-url-loader?{"sourceMap":true}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true,"sourceComments":true}!./node_modules/import-glob!./resources/assets/styles/main.scss
* ./fonts/roboto-v29-latin-regular.woff in ./node_modules/cache-loader/dist/cjs.js!./node_modules/css-loader?{"sourceMap":true}!./node_modules/postcss-loader/dist/cjs.js?{"postcssOptions":{"path":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets/build","ctx":{"open":true,"copy":"images/**/*","proxyUrl":"http://localhost:3000","cacheBusting":"[name]_[hash:8]","paths":{"root":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe","assets":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets","dist":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist"},"enabled":{"sourceMaps":true,"optimize":false,"cacheBusting":false,"watcher":true},"watch":["app/**/*.php","config/**/*.php","resources/views/**/*.php"],"entry":{"main":["./scripts/main.js","./styles/main.scss"],"customizer":["./scripts/customizer.js"]},"publicPath":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist/","devUrl":"http://localhost:8888/jameslanmandotcom","env":{"production":false,"development":true},"manifest":{}}},"sourceMap":true}!./node_modules/resolve-url-loader?{"sourceMap":true}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true,"sourceComments":true}!./node_modules/import-glob!./resources/assets/styles/main.scss
* ./fonts/roboto-v29-latin-regular.woff2 in ./node_modules/cache-loader/dist/cjs.js!./node_modules/css-loader?{"sourceMap":true}!./node_modules/postcss-loader/dist/cjs.js?{"postcssOptions":{"path":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets/build","ctx":{"open":true,"copy":"images/**/*","proxyUrl":"http://localhost:3000","cacheBusting":"[name]_[hash:8]","paths":{"root":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe","assets":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/resources/assets","dist":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist"},"enabled":{"sourceMaps":true,"optimize":false,"cacheBusting":false,"watcher":true},"watch":["app/**/*.php","config/**/*.php","resources/views/**/*.php"],"entry":{"main":["./scripts/main.js","./styles/main.scss"],"customizer":["./scripts/customizer.js"]},"publicPath":"/Applications/MAMP/htdocs/jameslanmandotcom/wp-content/themes/Circe/dist/","devUrl":"http://localhost:8888/jameslanmandotcom","env":{"production":false,"development":true},"manifest":{}}},"sourceMap":true}!./node_modules/resolve-url-loader?{"sourceMap":true}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true,"sourceComments":true}!./node_modules/import-glob!./resources/assets/styles/main.scss

Have you made any changes to your webpack.config.js files? Is this a fresh Sage theme, or one you’ve been working on?

New install as of October 12th but one I have been working on. No changes made to webpack.config.js file!

Have you changed the discussion title so that it includes the Sage version now?
I didn’t know you are using Sage 9, I assumed you are using Sage 10.

Yes - Alwaysblank did. Again, apologies for the confusion - I thought I had upgraded.

  1. There is a Sage 9.x update branch that fixes lots of build issues:
    Sage "9.1": Please test

  2. When you host the font yourself you will want to minify and subset the font files.
    When you are using different weights and variants of the same font you should try to use a so called variable font, which is well-supported now.

  3. It is possible to add a webpack-loader for font files but I found it much easier to optimize the font files once and then use webpack copy to just copy them over to dist. There is already stuff copied in webpack, so you just have to duplicate that code in the webpack js and adjust the glob (regexp) so it matches font files.

1 Like