Adding multiple local fonts to NextJs 13 - javascript

I'm trying to add multiple local fonts to nextjs 13.
The documentation says to do the following for a single file.
I tried importing the two files in the following way:
import '#/styles/globals.css';
import localFont from '#next/font/local';
const myFonts = localFont({
src: '../public/Fonts/AnultraSlabRegular.ttf',
variable: '--Anultra-Slab',
});
const myFonts2 = localFont({
src: '../public/Fonts/IntroRegular.ttf',
variable: '--Intro-Regular',
});
export default function App({ Component, pageProps }) {
return (
<main className={'${myFonts2.className} ${myFonts.className}'}>
<Component {...pageProps} />
</main>
);
}
this method did not work when assigning a font-family to specific elements.
Thanks in advance!

Are you using Tailwind? I see you are defining a variable for the font but not using it here, and there are a couple of extra steps to get Tailwind wired up.
The only syntax issue I see is the backticks on the class name that you already mentioned.
import '#/styles/globals.css';
import localFont from '#next/font/local';
const myFonts = localFont({
src: '../public/Fonts/AnultraSlabRegular.ttf',
variable: '--Anultra-Slab',
});
const myFonts2 = localFont({
src: '../public/Fonts/IntroRegular.ttf',
variable: '--Intro-Regular',
});
export default function App({ Component, pageProps }) {
return (
<main className={`${myFonts2.className} ${myFonts.className}`}>
<Component {...pageProps} />
</main>
);
}
you can also use the variable you defined in your CSS once you have them loaded. It should look something like:
button {
font-family: var(--Anultra-Slab);
}
I had similar issues where the font was being loaded but not applied because a global style was overriding the font-family to nothing, making it default to the system font. I expanded font-family in the "computed" view in dev tools to find the issue, then added font-family: inherit to the offending class to fix it.
You should be able to apply the font-family: var(--Anultra-Slab) in the browser dev tools to make sure the font is being loaded correctly, poke around, and locate the issue.
Hope this helps!

If you are using tailwind then this is how I did it -
Firstly add the font faces at the top in your root styles file (probably named as either globals.css or style.css):
#font-face {
font-family: "IntroRegular";
src: url("/Fonts/IntroRegular.ttf");
font-weight: 400;
font-display: swap;
font-style: normal;
}
#font-face {
font-family: "AnultraSlabRegular";
src: url("/Fonts/AnultraSlabRegular.ttf");
font-weight: 400;
font-display: swap;
font-style: normal;
}
And then in your tailwind.config.js file, extend the
fontFamily to include your font like this:
theme: {
extend: {
fontFamily: {
IntroRegular: ["IntroRegular", "sans-serif"],
AnultraSlabRegular: ["AnultraSlabRegular", "sans-serif"]
}
}
}
Now, you can use these fonts just like you use tailwind ones, for example:
<main className="font-AnultraSlabRegular">

I had to read the documentation more than once. (Since it is referencing the variable name in the example using Google fonts.) It took me quite some time to only discover adding multiple classes should not be done by using the className (as most examples use) to apply one font like;
// Do not use className for importing multiple local fonts
<body className={`bg-black ${nantes.className} ${spezia.className}`}>
{children}
</body>
But by using the variable name like this;
// use variable for importing multiple fonts which you can use throughout the application
<body className={`bg-black ${nantes.variable} ${spezia.variable}`}>
{children}
</body>
To be complete, this is what I have right now, which works;
layout.tsx
import localFont from '#next/font/local'
const spezia = localFont({
src: '../fonts/FaroVariableWeb.woff2',
variable: '--font-spezia'
})
const nantes = localFont({
src: '../fonts/Nantes_Variable_Upright_Web.woff2',
variable: '--font-nantes'
})
import './globals.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body className={`bg-black ${nantes.variable} ${spezia.variable}`}>
{children}
</body>
</html>
)
}
global.css
#tailwind base;
#tailwind components;
#tailwind utilities;
html, body {
font-family: var(--font-spezia);
color:blueviolet;
}
h1 {
font-family: var(--font-nantes);
color: aqua;
}

Related

CSS Modules not applying style - next.js 13

Using next.js 13.1.1 with /app
I've been doing all of my styles through a global.css up until now, but I'm trying to migrate them into CSS Modules.
In my root layout.js, I have a Header component that imports from ./header.js. This works fine with global.css.
I've made a module ../styles/header.module.css, and I import it into ./header.js with import styles from "../styles/header.module.css";, but then when I try to use it, for example with <h1 className={styles.big_title}>, none of the CSS is applied.
Any help would be much appreciated, I've been trying to fix this for hours.
No errors occur, the CSS just doesn't apply.
// ./layout.js
import Header from "./header";
import Footer from "./footer";
import "../styles/global.css";
export default function RootLayout({ children }) {
return (
<html>
<head>
<title>SB-Wiki</title>
</head>
<body>
<Header />
<main>
{children}
</main>
<Footer />
</body>
</html>
);
}
/* ../styles/header.module.css */
.big_title {
color: red;
}
// ./header.js
import styles from "../styles/header.module.css";
export default function Header() {
return (
<header>
<p className={styles.big_title}>Test</p>
</header>
)
}
The styles display if I add import styles from "../styles/header.module.css"; to the top of a page.js but that rather defeats the point of having a root layout.js as the framework is designed this way.
The problem is with next.js . You need to create config file for next.config.js in order to properly support your CSS .
module.exports = {
cssModules: true,
cssLoaderOptions: {
importLoaders: 1,
localIdentName: "[]",
},
};
The config file will tell the next.js to treat your CSS files as CSS
You can use it like this styles["big_title"]
// ./header.js
import styles from "../styles/header.module.css";
export default function Header() {
return (
<header>
<p className={styles["big_title"]}>Test</p>
</header>
)
}

Cannot override CSS Module class style with more specific one

I'm having a problem applying a CSS Module to a React Component (in an app created with 'create-react-app' and using webpack). I have an element that has a 'className' and an 'id' attribute. The 'className' style get applied, but when I attempt to 'override' it with a more specific 'id' style, it doesn't take. My Use Case is that the more-specific style override is in a separate file, but the problem exists with the two styles in the same CSS file.
Here's my component:
import React from 'react';
import styles from './styles.module.css'
class QuestionTag extends React.Component {
render() {
let c1 = "qumultiline";
return (
<>
<div className={`${styles[c1]}`} id={`${c1}`}>
Hello there
</div>
</>
);
}
}
export default QuestionTag;
This is how the element gets rendered:
<div class="styles_qumultiline__iXUv1" id="qumultiline">Hello there</div>
Here's styles.module.css:
.qumultiline {
background-color: #006699;
color: white;
}
div#qumultiline {
background-color: red;
color: red;
}
Is there something special I need to do to be able to override the 'className'?

How to use a CSS framework with LitElement

I would like to use the CSS framework Bulma with LitElement. I know I can use an External Stylesheet However, they state it is bad practice to do it this way. I also have the problem that I had to import it into each element, which doesn't feel right.
So I copied the whole Bulma file content into a js module, but the styles are not applied after importing it.
import { css } from 'lit-element'
export default css`
#-webkit-keyframes spinAround {
from {
transform: rotate(0deg);
}
...
Importing the style as link tag works but is as mentioned bad practice.
import { LitElement, html, css } from 'lit-element'
import './src/table.js'
import styles from './styles.js'
class LitApp extends LitElement {
constructor() {
super()
this.tHeader = ['status', 'name', 'alias']
this.data = [
['connect', 'john', 'jdoe'],
['disconnect', 'carol', 'carbon'],
['disconnect', 'mike', 'mkan'],
['disconnect', 'tina', 'tiba'],
]
}
static get styles() {
return [
styles, // does not work
css`
:host {
padding: 5vw;
min-height: 100vh;
}
table.table {
width: 100%;
}`
]
}
render() {
return html`
<link rel="stylesheet" href="./node_modules/bulma/css/bulma.min.css">
<div class="columns">
<div class="column is-8 is-offset-2">
<div class="card">
<div class="card-content">
<agent-table .theader=${this.tHeader} .data=${this.data}></agent-table>
</div>
</div>
</div>
</div>`
}
}
customElements.define('lit-app', LitApp)
Furthermore, the Table does not receive the styles and I had to import the file again, which I would like to avoid.
class AgentTable extends LitElement {
constructor() {
super()
this.tHeader = []
this.data = []
}
static get properties() {
return {
theader: { type: Array },
data: { type: Array },
}
}
render() {
return html`
<table class="table is-narrow">
<thead>
<tr>${this.tHeader.map((header) => html`<td class="is-capitalized">${header}</td>`)}</tr>
</thead>
<tbody>
${this.data.map((row) => html`<tr>
${row.map((cell) => html`<td class="is-capitalized">${cell}</td>`)}
</tr>`)}
</tbody>`
}
}
customElements.define('agent-table', AgentTable)
I generally struggle to find a good approach to apply a CSS framework. I read back and forth on this page https://lit-element.polymer-project.org/guide/styles but I don't get it to work.
Sorry, If this is a bit ambiguous or hard to understand I struggle to voice this question properly.
There are a few problems with importing styles with the link tag. This is why it's a bad practice:
A new stylesheet will be created every time an instance of your element is created. LitElement uses constructable stylesheets (if supported) to share a single stylesheet instance across all elements.
The stylesheet is loaded after the element is rendered, so there can be a flash-of-unstyled-content
The ShadyCSS polyfill won't work
The href attribute is relative to the main document
Adding few thousands css rules to a custom element that only needs a few it's not the way it's meant to be. CSS frameworks should be split by custom-elements.
That said, you can do what you did there and copy the whole Bulma inside a js module. The problem it's that you have to escape the character "" of that CSS.
css`
.breadcrumb li + li::before {
color: #b5b5b5;
content: "\0002f";
}
`
This doesn't work as expected but doesn't fail because template literals allow you to read the raw strings as they were entered, without processing escape sequences. So there aren't any JS errors.
Escape those and it'll be fine:
css`
.breadcrumb li + li::before {
color: #b5b5b5;
content: "\\0002f";
}
`
Note, the browser support for the below is currently awful. As far as I understand, it only runs on chrome by default. Firefox has it implemented, but you need to start Firefox with an additional flag.
In this post by the creator of svelte, the styling problem also comes up. There are some fascinating bits to take away from this whole discussion.
As hinted by some a while ago and already been under suspicion, I had another go at the problem.
So I am using adopted stylesheets now in this variant. As showcased here.
This seems like a legit way in some cases.
'use strict';
// define some web component
class WebComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.text = ''
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<!-- setting some custom css just to see if it stays -->
<style> p { padding: 1rem; background-color: pink; } </style>
<!-- this is expected to become large font size from tailwindcss -->
<p class="title is-size-1">${this.text}</p>`
}
}
customElements.define('web-component', WebComponent)
// create two instances of the component to see if adopted style
// will change for both, meaning they are both referencing the same
// thing in memory
const webComponentA = document.createElement('web-component')
webComponentA.text = 'cowsay'
const webComponentB = document.createElement('web-component')
webComponentB.text= 'web'
// construct a blank style sheet
const sheet = new CSSStyleSheet();
(async () => {
// get some css styles as text, maybe with fetch if purely in the browser
// or via module resolver like rollup & webpack
const url = 'https://cdn.jsdelivr.net/npm/bulma#0.9.2/css/bulma.min.css'
const response = await fetch(url)
await sheet.replace(await response.text());
// set the styles on both components and add them to the dom
webComponentA.shadowRoot.adoptedStyleSheets = [sheet];
webComponentB.shadowRoot.adoptedStyleSheets = [sheet];
document.body.append(webComponentA, webComponentB)
})().catch(console.warn);
// some time later, purge the style sheet
// and watch both components loosing the framework styles
setTimeout(() => sheet.replaceSync(``), 3000);
Unfortunately, one has to use a constructed style sheet like shown above. It doesn't work if you try to give the document root style sheet. I.E.
webComponent.shadowRoot.adoptedStyleSheets = [document.StyleSheet.item(0)]
DOMException: Failed to set the 'adoptedStyleSheets' property on 'ShadowRoot': Can't adopt non-constructed stylesheets.

Next.js custom class on body using _document.js

I'm using the example here
https://github.com/zeit/next.js#custom-document
and I want to change the custom_class on the body tag
I have tried passing props on calling components but nothing works. I want to add a 'dark' class based on some condition but I have no idea how to change this.
EDIT:
I'm not sure this is possible. After getting help from the nextjs slack channel I was told
"Pages get rendered client side with next/link
And body is not touched after server side rendering"
I'm going to try and wrap things in another tag that I generate and try and change that.
The cleanest solution I found is not declarative, but it works well:
import Head from "next/head"
class HeadElement extends React.Component {
componentDidMount() {
const {bodyClass} = this.props
document.querySelector("body").classList.add(bodyClass || "light")
}
render() {
return <Head>
{/* Whatever other stuff you're using in Head */}
</Head>
}
}
export default HeadElement
With this Head component you would pass in "dark" or "light" (following the question's example for light/dark themes) as the bodyClass prop from the page.
As of current Next (10) and React (17) versions, If you'd like to change the body class from a page, you can can do it like this:
// only example: maybe you'll want some logic before,
// and maybe pass a variable to classList.add()
useEffect( () => { document.querySelector("body").classList.add("home") } );
Please note that useEffect is a Hook, so it can be used only in modern function components, not class ones.
The useEffect Hook can be used instead of the 'old' componentDidMount LifeCycle.
https://reactjs.org/docs/hooks-effect.html
https://reactjs.org/docs/hooks-overview.html
https://reactjs.org/docs/components-and-props.html
The only way to directly access the body tag on Next js is via the _document.js file but this file is rendered server side as stated in the Documentation.
The work around I suggest is to access the body tag from the component directly. Example:
const handleClick = (e) => {
document.querySelector('body').classList.toggle('dark')
}
<div onClick={handleClick}>Toggle</div>
The solution I came up with for my situation where I don't need a lot of unique body classes was to create a component called BodyClass.js and import that component into my Layout.js component.
BodyClass.js
import { Component } from 'react';
class BodyClass extends Component {
componentDidMount() {
if (window.location.pathname == '/') {
this.setBodyClass('home');
} else if (window.location.pathname == '/locations') {
this.setBodyClass('locations');
}
}
setBodyClass(className) {
// remove other classes
document.body.className ='';
// assign new class
document.body.classList.add(className);
}
render() {
return null;
}
}
export default BodyClass;
Layout.js
import Header from './Header';
import Footer from './Footer';
import BodyClass from '../BodyClass';
const Layout = ({ children }) => {
return (
<React.Fragment>
<BodyClass />
<Header />
{children}
<Footer />
</React.Fragment>
)
}
export default Layout;
Unfortunately, next/head does not allow specifying body class like React Helmet does:
<Helmet>
<body className="foo"/>
</Helmet>
Luckily, you can use this.props.__NEXT_DATA__.props.pageProps inside _document.js to get access to the page props and use them to to set the class on the <body> element:
import Document, { Html, Head, Main, NextScript } from 'next/document';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
const pageProps = this.props?.__NEXT_DATA__?.props?.pageProps;
console.log('pageProps => ', pageProps);
return (
<Html>
<Head />
<body className={pageProps.bodyClassName}>
<Main />
<NextScript />
</body>
</Html>
);
}
}
More info here.
Directly, it's not possible. But with another way, you can use framework like tailwind and insert the class directly in your css.
Here an example using tailwind:
.dark {
background-color: black;
color: white;
}
body {
#apply dark
}
It's not exactly the answer you are looking for, but I was able to accomplish the same thing functionally by passing a prop through the component which then updates a class on a top-level element that wraps everything in my app and sits just under the . This way each page can have it's own unique class and operates the same way I'd use a body class.
Here is where I pass the prop through
<Layout page_title={meta_title} page_desc={meta_desc} main_class={'contact_page'}>
And here is where I use it in the Layout component:
<main className={props.main_class}>
<Header page_title={props.page_title} page_desc={props.page_desc} />
{props.children}
<Footer />
</main>
This is my CSS only solution:
(I first looked at https://stackoverflow.com/a/66358460/729221 but that felt too complex for changing a bit of styling.)
(This uses Tailwind CSS, but does not need to.)
index.css:
body:has(#__next .set-bg-indigo-50-on-body) {
#apply bg-indigo-50;
}
layout.tsx:
//…
return (
<div className="set-bg-indigo-50-on-body">{children}</div>
)
This will work, as long as that div is a direct parent of <div id="__next">. Otherwise you need to update the css :has-rule.

Can I set styles to document body from styled-components?

I want to know if there is a possibility to cast styles from styled-components to wrapping element, like <body> tag in the way that looks like this:
class SomePageWrapper = styled.div`
body {
font-size: 62.5%
}
`
No, but you can use the global inject function to set stuff on your body like this:
import { injectGlobal } from 'styled-components';
injectGlobal`
#font-face {
font-family: 'Operator Mono';
src: url('../fonts/Operator-Mono.ttf');
}
body {
margin: 0;
}
`;
The example is from here: https://www.styled-components.com/docs/api#injectglobal
As it turns out - you can't set styled components to outer elements. This violates the philosophy of encapsulation - a benefit from styled-components.
So the way to do this would be to add a new class to body element called classList via JS in the parent component with componentDidMount() and remove it with componentWillUnmount().
For v4 styled-components, use createGlobalStyle .
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
body {
color: ${props => (props.whiteColor ? 'white' : 'black')};
}
`
// later in your app
<React.Fragment>
<GlobalStyle whiteColor />
<Navigation /> {/* example of other top-level stuff */}
</React.Fragment>
I think you can't do that.
class SomePageWrapper = styled.body`
font-size: 62.5%
`
So,when you put <SomePageWrapper/> in the render,it turns to be <body></body>.Then you need to put it in the root part,to replace <body>.
But how do you replace the <body> you have in index.html.When you can't replace it,you will end up two <body> in browser,or something weird will happen.
Just simply use css file for

Categories

Resources