Loading fonts with webpack - css

I'm trying to use custom fonts in a Project website with angular4.
This is my Project structure
This is my webpack.config.js
const isDevBuild = !(env && env.prod);
const sharedConfig = {
stats: { modules: false },
context: __dirname,
resolve: { extensions: [ '.js', '.ts' ] },
output: {
filename: '[name].js',
publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
},
module: {
rules: [
{ test: /\.ts$/, include: /ClientApp/, use: ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] },
{ test: /\.html$/, use: 'html-loader?minimize=false' },
{ test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' },
// Font Definitions
{ test: /\.svg$/, loader: 'url?limit=65000&mimetype=image/svg+xml&name=font/[name].[ext]' },
{ test: /\.woff$/, loader: 'url?limit=65000&mimetype=application/font-woff&name=font/[name].[ext]' },
{ test: /\.woff2$/, loader: 'url?limit=65000&mimetype=application/font-woff2&name=font/[name].[ext]' },
{ test: /\.[ot]tf$/, loader: 'url?limit=65000&mimetype=application/octet-stream&name=font/[name].[ext]' },
{ test: /\.eot$/, loader: 'url?limit=65000&mimetype=application/vnd.ms-fontobject&name=font/[name].[ext]' }
]
},
plugins: [new CheckerPlugin()]
};
This is my css with #font-face
#font-face {
font-family: "FuturaMaxiLight";
src: url('/fonts/FuturaMaxi/Futura-Maxi-Light.eot') format('embedded-opentype'), /*for IE */
url('/fonts/FuturaMaxi/Futura-Maxi-Light.ttf') format('truetype'), /* for CSS3 browsers */
url('/fonts/FuturaMaxi/Futura-Maxi-Light.woff') format('woff');
font-weight: normal;
font-style: normal;
}
#font-face {
font-family: "FuturaMaxiDemi";
src: url('/fonts/FuturaMaxi/Futura-Maxi-Demi.eot') format('embedded-opentype'), /*for IE */
url('/fonts/FuturaMaxi/Futura-Maxi-Demi.ttf') format('truetype'), /* for CSS3 browsers */
url('/fonts/FuturaMaxi/Futura-Maxi-Demi.woff') format('woff');
font-weight: normal;
font-style: normal;
}
#font-face {
font-family: "FuturaMaxiBold";
src: url('/fonts/FuturaMaxi/Futura-Maxi-Bold.eot') format('embedded-opentype'), /*for IE */
url('/fonts/FuturaMaxi/Futura-Maxi-Bold.ttf') format('truetype'), /* for CSS3 browsers */
url('/fonts/FuturaMaxi/Futura-Maxi-Bold.woff') format('woff');
font-weight: normal;
font-style: normal;
}
This is the error when I try like that
If I change the css and try to include a dot before the import.
1 dot gives errors.
2 dots breaks the app.
#font-face {
font-family: "FuturaMaxiBold";
src: url('./fonts/FuturaMaxi/Futura-Maxi-Bold.eot') format('embedded-opentype'), /*for IE */
url('./fonts/FuturaMaxi/Futura-Maxi-Bold.ttf') format('truetype'), /* for CSS3 browsers */
url('./fonts/FuturaMaxi/Futura-Maxi-Bold.woff') format('woff');
font-weight: normal;
font-style: normal;
}

Your loaders are specifying name=font/[name].[ext] and your css is looking at /fonts/FuturaMaxi/[name].[ext].
Try changing the loaders to use name=[path][name].[ext] or name=fonts/FuturaMaxi/[name].[ext]

You need resolve-url-loader to resolve the correct path in your build.
See https://github.com/bholloway/resolve-url-loader
Install resolve-url-loader
npm install --save-dev resolve-url-loader
Modify your Webpack CSS rule:
{
test: /\.css$/,
use: [
'to-string-loader',
isDevBuild ? 'css-loader' : 'css-loader?minimize',
'resolve-url-loader' // Add this
]
}
Use relative paths in your CSS files
#font-face {
font-family: "FuturaMaxiLight";
src: url('../fonts/FuturaMaxi/Futura-Maxi-Light.eot') format('embedded-opentype'), /*for IE */
url('../fonts/FuturaMaxi/Futura-Maxi-Light.ttf') format('truetype'), /* for CSS3 browsers */
url('../fonts/FuturaMaxi/Futura-Maxi-Light.woff') format('woff');
font-weight: normal;
font-style: normal;
}

Related

Inlining fonts and images inside CSS files with Webpack5?

I need to import into my project some CSS files with Webpack 5 and I need to inline all these resources (it's a requirement sadly). Inside the CSS there are some fonts and images with relative URI, like this:
#font-face { font-family: "MyFont"; src: url(./fonts/Roboto-Regular.ttf) format("truetype"); font-weight: normal;}
#font-face { font-family: "MyFont"; src: url(./fonts/Roboto-Bold.ttf) format("truetype"); font-weight: bold;}
#font-face { font-family: "MyFont"; src: url(./fonts/Roboto-Italic.ttf) format("truetype"); font-weight: normal; font-style: italic;}
#font-face { font-family: "MyFont"; src: url(./fonts/Roboto-BoldItalic.ttf) format("truetype"); font-weight: bold; font-style: italic;}
#font-face { font-family: 'Material Icons'; font-style: normal; font-weight: 400; src: url(./fonts/material-icons.woff2) format('woff2'); }
#font-face { font-family: 'Material Icons Outlined'; font-style: normal; font-weight: 400; src: url(./fonts/material-icons-outlined.woff2) format('woff2'); }
* { font-family: "MyFont", "Roboto-Light", "Noto Sans CJK SC", "DejaVu Sans"; }
.UICheckbox { width:80px; height:89px; background-image:url("img/checkboxOFF.png"); background-repeat:no-repeat; }
.UICheckbox.checked { background-image:url("img/checkboxON.png"); }
Since I need to import as base64 the CSS files I cannot actually process automatically the resources found inside of them (contrary to how it is done with PostCSS or similiars).
My current webpack configuration is the following but it just ignores the url() statements:
{
test: /\.(png|jpg|gif)$/i,
type: "asset/inline",
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: "asset/inline",
},
{
test: /\.css$/i,
type: "asset/inline",
},
Is there a better way to handle this?
I found a solution that is not really generic or solid but does the job, at least in my case scenario.
The imports are relatives to a fixed source path so the idea is to read the resources found inside the url() rules and process it as DataURI in base64 encoding.
I found quite useful the use of datauri which provides a way to include data in-line as if they were external resources and manages the mimetypes automatically.
npm install datauri --save
Then I had to modify the generator handler inside webpack.config.js to process the resources manually exploiting the datauri package.
const path = require("path");
const Datauri = require("datauri/sync");
const EXTERNAL_ROOT_PATH = "./src/external/dev/";
module.exports = {
...
module: {
rules: [
{
test: /\.css$/i,
type: "asset/inline",
generator: {
dataUrl: (content) => {
content = content.toString();
// Get the resource paths inside the CSS url() rules
let asset_urls = [];
let match,
regex = /url\((.*?)\)/gi;
while ((match = regex.exec(content))) {
asset_urls.push(match[1]);
}
// console.log(asset_urls);
// Convert the resource to a DataURI and replace it inside url()
asset_urls.forEach((file_path) => {
// Sanitize the file path first
sanitized_file_path = file_path.replace(/[\"\']/g, "").replace(/^(?:\.\.\/)+/, "");
const data_uri = Datauri(path.join(EXTERNAL_ROOT_PATH, sanitized_file_path));
// console.log(data_uri.content); //=> "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
// console.log(data_uri.mimetype); //=> "image/png"
// console.log(data_uri.base64); //=> "iVBORw0KGgoAAAANSUhEUgAA..."
// console.log(data_uri.buffer); //=> file buffer
content = content.replace(file_path, data_uri.content);
});
return "data:text/css;base64," + Buffer.from(content).toString("base64");
},
},
},
],
},
};

Load local fonts in vite vue3 project

In main.scss I load local fonts from assets/styles/fonts folder:
#font-face {
font-family: 'Opensans-Bold';
font-style: normal;
src: local('Opensans-Bold'), url(./fonts/OpenSans-Bold.ttf) format('truetype');
}
#font-face {
font-family: 'Opensans-Light';
font-style: normal;
src: local('Opensans-Light'), url(./fonts/OpenSans-Light.ttf) format('truetype');
}
then in vite.config I load main.scss:
css: {
preprocessorOptions: {
scss: {
additionalData: `#import "#/assets/styles/main.scss";`
}
}
},
but all css from main.scss is applied except fonts, I get error:
downloadable font: download failed (font-family: "Opensans-Bold" style:normal weight:400 stretch:100 src index:1): status=2152398850 source: http://localhost:3000/fonts/OpenSans-Bold.ttf
Am I on right track or I need some other approach (similar works with Vue-CLI)?
That was the right way, the solution is the relative path so:
src: local('Opensans-Bold'), url(#/assets/styles/fonts/OpenSans-Bold.ttf) format('truetype');
and setting alias in vite.config.js :
resolve: {
alias: {
'#': path.resolve(__dirname, 'src'),
}
}
I was able to make this work by simply put the fonts on a public folder on root.
like:
public/.**.ttf
src/

Fonts not working when loaded by css modules

I'm using css modules with sass. So i try to load them like this:
#font-face {
font-family: 'Book Antiqua';
src: url(#/assets/fonts/book_antiqua.ttf) format('truetype');
font-weight: 400;
font-style: normal;
}
#font-face {
font-family: 'Montserrat';
src: url(#/assets/fonts/Montserrat/Montserrat-Regular.ttf) format('truetype');
font-weight: 400;
font-style: normal;
}
The most interesting path is that they actually loaded by the browser.
So inline css looks like this:
#font-face {
font-family: 'Book Antiqua';
src: url("http://localhost:8080/static/media/book_antiqua.d8ae129c.ttf") format("truetype");
font-weight: 400;
font-style: normal; }
#font-face {
font-family: 'Montserrat';
src: url("http://localhost:8080/static/media/Montserrat-Regular.2bb14503.ttf") format("truetype");
font-weight: 400;
font-style: normal; }
But they do not work( So i understand nothing. Browser loaded fonts, font-face syntax looks OK, but they do not trigger.
My webpack config's loaders:
rules: [
{
exclude: [
/\.html$/,
/\.(js|jsx|ts|tsx)$/,
/\.css$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
/\.scss$/,
],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000',
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
},
},
'postcss-loader',
],
}),
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
localIdentName: '[name]__[local]___[hash:base64:5]',
},
},
'sass-loader',
],
}),
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
{
test: /\.(ttf|eot|woff|woff2)$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
},
},
},
}]
I never import fonts from my assets, I always use services like google fonts to get mine, they have montserrat there and I import it like this
#import url('https://fonts.googleapis.com/css?family=Montserrat');
But that's just a band-aid, another place were a problem could be found is where do you use that font, how do you add that font to a certain class is important, for example for montserrat you should do it like this
font-family: 'Montserrat', sans-serif;

#import syntax inside css file not resolving font file

I am working on a react application and trying to implement CSS + Fonts.
In my react component CSS file I have regular CSS and at the very top I import my mixins
#import '../../mixins.css';
/*CSS*/
Inside my mixin file I have
mixin.css
#font-face {
font-family: 'SourceSansPro-Black';
src: url("./SourceSansPro-Black.ttf") format('truetype');
font-weight: normal;
font-style: normal;
}
Webpack throws an error
Error: Cannot resolve 'file' or 'directory' ./SourceSansPro-Black.ttf
This is my webpack config module loadres
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel',
include: [
path.resolve(__dirname, 'src/app')
]
},
{
test: /\.css$/,
include: [
path.resolve(__dirname, 'src/app/styles'),
path.resolve(__dirname, 'src/app/components')
],
loader: combineLoaders([
{
loader: 'style'
},
{
loader: 'css',
query: {
modules: true,
sourceMap: true,
localIdentName: '[folder]__[local]--[hash:base64:10]'
}
},
{
loader: 'postcss'
}
])
},
// Font Definitions
{
test: /\.ttf$/,
include: [
path.resolve(__dirname, 'src/app/assets/fonts')
],
loader: 'url',
query: {
limit: 50000,
mimetype: 'application/octet-stream',
name: './fonts/[name].[ext]'
}
}
]
},
and the postcss
postcss: (_webpack) => {
return [
require('postcss-import')({
addDependencyTo: _webpack,
path: [ 'styles', 'components'],
root: path.resolve(__dirname, 'src/app'),
skipDuplicates: true
}),
require('postcss-cssnext')()
];
},
I dont' understand why the url isn't resolving correctly.
UPDATE
I have a font.css file that looks like this
#font-face {
font-family: 'SourceSansPro-Bold';
src: url('./SourceSansPro-Bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
#font-face {
font-family: 'SourceSansPro-Regular';
src: url('./SourceSansPro-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
#font-face {
font-family: 'SourceSansPro-Light';
src: url('./SourceSansPro-Light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
I then import the font.css into a mixins.css file
#import '../fonts/fonts.css';
:root {
--regular: {
font-family: 'SourceSansPro-Regular';
font-style: normal;
font-weight: normal;
}
}
Then in my react component I have this:
#import 'styles/mixins.css';
.regular {
#apply --regular;
}
There is no error (or warning) that states any issues with loading the fonts via the url().
webpack.config
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel',
include: [
path.resolve(__dirname, 'src/app')
]
},
{
test: /\.css$/,
include: [
path.resolve(__dirname, 'src/app/assets/fonts'),
path.resolve(__dirname, 'src/app/assets/styles'),
path.resolve(__dirname, 'src/app/components')
],
loader: combineLoaders([
{
loader: 'style',
},
{
loader: 'css',
query: {
modules: true,
sourceMap: true,
localIdentName: '[folder]__[local]--[hash:base64:10]'
}
},
{
loader: 'resolve-url',
query: {
sourceMap: true,
silent: false,
fail: true,
keepQuery: true
}
},
{
loader: 'postcss',
query: {
sourceMap: true
}
}
])
},
// Font Definitions
{
test: /\.ttf$/,
include: [
path.resolve(__dirname, 'src/app/assets/fonts')
],
loader: 'url',
query: {
limit: 100000,
mimetype: 'application/octet-stream',
name: './fonts/[name].[ext]'
}
}
]
}
An interesting thing is that if I use the ExtractTextWebpackPlugin on the CSS the above works, the fonts get rendered. If I do it this way the tradeOff is there is no CSS hot reloading.
Any suggestions on how to get the fonts to load without the extract-text plugin?

woff and ttf fonts 404 not found

I'm using webpack to compile my sass files.
I have a font-face that looks like this:
#font-face
font-family: "Alef"
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.eot")
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.eot?#iefix") format("embedded-opentype"), url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.woff") format("woff"), url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.ttf") format("truetype"), url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.svg#alefbold") format("svg")
font-weight: bold
font-style: normal
and this is how the loaders in my webpack config file looks like
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)(\?[a-z0-9=&.]+)?$/,
loader: 'file?name=assets/[name].[hash].[ext]'
},
{
test: /\.sass$/,
exclude: /node_modules/,
loader: 'raw-loader!sass-loader'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw'
}
but, i'm getting 404 on the ttf and woff, so this font appears only in chrome (doesn't appear in firefox and edge)
thanks!
Solved! I separated the srcs, like that:
#font-face
font-family: "Alef"
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.eot")
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.eot?#iefix") format("embedded-opentype")
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.woff") format("woff")
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.ttf") format("truetype")
src: url("..\..\public\fonts\Alef\Alef-Webfont\Alef-Bold.svg#alefbold") format("svg")
font-weight: bold
font-style: normal

Resources