Inlining fonts and images inside CSS files with Webpack5? - css

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");
},
},
},
],
},
};

Related

Local font not loading in NextJs

I am trying to load the local font 'Stigfier' through my 'styles/styles.css' global css and it is not loading. My google fonts loaded in the '_document.jsx' work fine.
#font-face {
font-family: Stigfier;
src:
url('/../public/fonts/stigfier/Stigfier.woff') format('woff'),
url('/../public/fonts/stigfier/Stigfier.woff2') format('woff2'),
url('/../public/fonts/stigfier/Stigfier.ttf') format('truetype'),
url('/../public/fonts/stigfier/Stigfier.otf') format('opentype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
body {
font-family: Stigfier;
}
But it is not loading, same with the recommended way in the Nextjs website docs:
import localFont from '#next/font/local'
const stigfier = localFont({src: '/../public/fonts/stigfier/Stigfier.woff2'})
export default function MyApp({ Component, pageProps }) {
return (
<div className={stigfier.className}>
<Component {...pageProps} />
</div>
)
}
and even creating a link in the '_document.jsx' like so:
<link
rel="preload"
href="/../public/fonts/stigfier/Stigfier.ttf"
as="font"
type="font/ttf"
crossOrigin="anonymous"
/>
As your fonts are already in the public folder you do not need to specify it.
#font-face {
font-family: Stigfier;
src:
url('/fonts/stigfier/Stigfier.woff') format('woff'),
url('/fonts/stigfier/Stigfier.woff2') format('woff2'),
url('/fonts/stigfier/Stigfier.ttf') format('truetype'),
url('/fonts/stigfier/Stigfier.otf') format('opentype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
body {
font-family: Stigfier;
}```
In next/font/local, with src you need to add other properties like weight, display and subset.
also make sure you are working on next 13, because next/font is a new feature which comes under nextjs 13.
read this: Nextjs 13 features

How do you define #font-face in a CSSObject?

Suppose I have the following #font-face rule(s) and I want to put them as a CSSObject instead of a flat string.
#font-face {
font-family: 'Blah';
font-weight: 400;
font-style: normal;
src:
url('./Blah-Normal.woff2') format('woff2'),
url('./Blah-Normal.woff') format('woff');
}
#font-face {
font-family: 'Blah';
font-weight: 700;
font-style: bold;
src:
url('./Blah-Bold.woff2') format('woff2'),
url('./Blah-Bold.woff') format('woff');
}
...
How can I define it as a CSSObject? I'm trying to pass it over to Mui's MuiCssBaseline and it takes a CSSObject or string. I'd like to pass a CSSObject so that I could add more CSS styling on it more easily and easier to read. But right now it is just a flat string containing only the #font-face definitions.
// import { CSSObject } from '#mui/material';
// import { CSSObject } from '#emotion/styled';
import { CSSObject } from 'styled-components';
const BlahCSSObject = {
'#font-face': {
// magic?
src: [
// it definitely don't like the following format
url('./Blah-Bold.woff2') format('woff2'),
url('./Blah-Bold.woff') format('woff')
]
},
'#font-face': { // would cause dupe keys no?
...
},
...
} as CSSObject;
Thanks!
I think you can use css method from styled-components.
import { css } from 'styled-components'
const myOwnStyle = css`
#font-face {
// styles
}
`

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/

Loading fonts with webpack

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;
}

Font-face not working at all

I've read a lot of posts and make my font-face code same as in other answers and it's still not working...
#font-face {
font-family: 'KlavikaLT';
src: url('Klavika-Light.otf') format('opentype');
}
#font-face {
font-family: 'KlavikaLTit';
src: url('Klavika-LightItalic.otf') format('opentype');
}
#font-face {
font-family: 'KlavikaBD';
src: url('Klavika-Bold.otf') format('opentype');
}
#font-face {
font-family: 'KlavikaBDit';
src: url('Klavika-BoldItalic.otf') format('opentype');
}
#font-face {
font-family: 'KlavikaRG';
src: url('Klavika-Regular.otf') format('opentype');
}
.footerleft
{
font-family: KlavikaLT;
}
.footerright
{
font-family: KlavikaBDit;
}
This is my example code that I use. Fonts are in the same folder as stylesheet file. Where do I make a mistake?
EDIT 1
It's funny because it's working on my localhost and all paths are correct. Not working on my server, but I made the same as in my code on other my site and it's working there...
Use this syntax:
<style>
#font-face {
font-family: 'KlavikaLT';
src: url('KlavikaLT.eot');
src: url('KlavikaLT.eot?#iefix') format('embedded-opentype'),
url('KlavikaLT.woff') format('woff'),
url('KlavikaLT.ttf') format('truetype'),
url('KlavikaLT.svg#KlavikaLT') format('svg');
font-weight: normal;
font-style: normal;
}
body { font-family: "KlavikaLT", serif; }
</style>
Make sure the file paths are correct.
This is the specification for #font-face:
#font-face {
[font-family: <family-name>;]?
[src: [ <uri> [format(<string>#)]? | <font-face-name> ]#;]?
[unicode-range: <urange>#;]?
[font-variant: <font-variant>;]?
[font-feature-settings: normal|<feature-tag-value>#;]?
[font-stretch: <font-stretch>;]?
[font-weight: <weight>];
[font-style: <style>];
}
'KlavikaLT' is not a standard font like Courier for example. Your browser has no idea what that font is unless you tell it.
You can tell the browser about your font in a couple of ways:
Post the import at the top of your css:
#import url(https://fonts.googleapis.com/css?family=Open+Sans);
Import with HTML, above your CSS:
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
Or using JavaScript:
<script type="text/javascript">
WebFontConfig = {
google: { families: [ 'Open+Sans::latin' ] }
};
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})(); </script>
Examples copied from Google Fonts.
If you're using a custom font hosted on your own server, you can use the above syntax, but replace the urls with your own. Each one should contain the #font-face, which looks like this:
#font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2) format('woff2');
}
Just added more extensions of font, like .ttf and .woff. Now it's working perfectly, but don't know why .otf doesn't work at all. Cheers!
#font-face
{
font-family: KlavikaLT;
src: url('fonts/KlavikaLT.otf') format('opentype'),
url('fonts/KlavikaLT.ttf') format('truetype'),
url('fonts/KlavikaLT.woff') format('woff');
}

Resources