mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 11:07:04 +00:00
feat(fonts): apply per-book body and heading fonts on screen
Split heading typography onto a new --font-heading token and set both --font-book and --font-heading from the active book's fonts when it opens or its setup changes. Declare @font-face for the new bundled fonts.
This commit is contained in:
1 parent
124c1947c1
commit
28367b6670
5 files changed
+101
-6
No files matched your search
@@ -0,0 +1,13 @@
|
|||||||
|
import { type BookFonts, fontStack } from "./model/fonts";
|
||||||
|
|
||||||
|
export function applyBookFonts(fonts: BookFonts): void {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.style.setProperty("--font-book", fontStack(fonts.body));
|
||||||
|
root.style.setProperty("--font-heading", fontStack(fonts.heading));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetBookFonts(): void {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.style.removeProperty("--font-book");
|
||||||
|
root.style.removeProperty("--font-heading");
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from "../model/book";
|
} from "../model/book";
|
||||||
import { loadActiveChapter, saveActiveChapter } from "../editor/positions";
|
import { loadActiveChapter, saveActiveChapter } from "../editor/positions";
|
||||||
import { saveBook } from "../library";
|
import { saveBook } from "../library";
|
||||||
|
import { applyBookFonts, resetBookFonts } from "../book-style";
|
||||||
|
|
||||||
export const COVER_ID = "__cover__";
|
export const COVER_ID = "__cover__";
|
||||||
|
|
||||||
@@ -60,10 +61,12 @@ export const useBook = create<BookState>((set, get) => ({
|
|||||||
const saved = loadActiveChapter(normalized.id);
|
const saved = loadActiveChapter(normalized.id);
|
||||||
const valid = saved === COVER_ID || normalized.chapters.some((c) => c.id === saved);
|
const valid = saved === COVER_ID || normalized.chapters.some((c) => c.id === saved);
|
||||||
const activeChapterId = valid ? (saved as string) : normalized.chapters[0]?.id ?? "";
|
const activeChapterId = valid ? (saved as string) : normalized.chapters[0]?.id ?? "";
|
||||||
|
applyBookFonts(normalized.settings.fonts);
|
||||||
set({ book: normalized, activeChapterId, dirty: false });
|
set({ book: normalized, activeChapterId, dirty: false });
|
||||||
},
|
},
|
||||||
closeBook: () => {
|
closeBook: () => {
|
||||||
flushOutgoing(get().book, get().dirty);
|
flushOutgoing(get().book, get().dirty);
|
||||||
|
resetBookFonts();
|
||||||
set({ book: null, activeChapterId: "", dirty: false });
|
set({ book: null, activeChapterId: "", dirty: false });
|
||||||
},
|
},
|
||||||
setActiveChapter: (id) => set({ activeChapterId: id }),
|
setActiveChapter: (id) => set({ activeChapterId: id }),
|
||||||
@@ -140,7 +143,12 @@ export const useBook = create<BookState>((set, get) => ({
|
|||||||
setMetadata: (patch) =>
|
setMetadata: (patch) =>
|
||||||
set((s) => (s.book ? { dirty: true, book: { ...s.book, metadata: { ...s.book.metadata, ...patch } } } : {})),
|
set((s) => (s.book ? { dirty: true, book: { ...s.book, metadata: { ...s.book.metadata, ...patch } } } : {})),
|
||||||
setSettings: (patch) =>
|
setSettings: (patch) =>
|
||||||
set((s) => (s.book ? { dirty: true, book: { ...s.book, settings: { ...s.book.settings, ...patch } } } : {})),
|
set((s) => {
|
||||||
|
if (!s.book) return {};
|
||||||
|
const book = { ...s.book, settings: { ...s.book.settings, ...patch } };
|
||||||
|
if (patch.fonts) applyBookFonts(book.settings.fonts);
|
||||||
|
return { dirty: true, book };
|
||||||
|
}),
|
||||||
setCover: (patch) =>
|
setCover: (patch) =>
|
||||||
set((s) => (s.book ? { dirty: true, book: { ...s.book, cover: { ...s.book.cover, ...patch } } } : {})),
|
set((s) => (s.book ? { dirty: true, book: { ...s.book, cover: { ...s.book.cover, ...patch } } } : {})),
|
||||||
markSaved: () => set({ dirty: false }),
|
markSaved: () => set({ dirty: false }),
|
||||||
|
|||||||
+14
-5
@@ -427,6 +427,7 @@ body.resizing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chapter-title {
|
.chapter-title {
|
||||||
|
font-family: var(--font-heading);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 31px;
|
font-size: 31px;
|
||||||
line-height: 1.16;
|
line-height: 1.16;
|
||||||
@@ -434,6 +435,10 @@ body.resizing {
|
|||||||
margin: 15px 0 0;
|
margin: 15px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose :is(h1, h2, h3) {
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
}
|
||||||
|
|
||||||
.prose p {
|
.prose p {
|
||||||
font-size: 19px;
|
font-size: 19px;
|
||||||
line-height: 1.68;
|
line-height: 1.68;
|
||||||
@@ -565,11 +570,15 @@ body.resizing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page .p-title {
|
.page .p-title {
|
||||||
font-family: var(--font-book);
|
font-family: var(--font-heading);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page :is(h2, h3) {
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
}
|
||||||
|
|
||||||
.page p {
|
.page p {
|
||||||
font-family: var(--font-book);
|
font-family: var(--font-book);
|
||||||
font-size: 6.6px;
|
font-size: 6.6px;
|
||||||
@@ -620,7 +629,7 @@ body.resizing {
|
|||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: var(--font-book);
|
font-family: var(--font-heading);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 31px;
|
font-size: 31px;
|
||||||
line-height: 1.16;
|
line-height: 1.16;
|
||||||
@@ -1432,7 +1441,7 @@ body.resizing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cover-title {
|
.cover-title {
|
||||||
font-family: var(--font-book);
|
font-family: var(--font-heading);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 9.5cqw;
|
font-size: 9.5cqw;
|
||||||
line-height: 1.08;
|
line-height: 1.08;
|
||||||
@@ -2108,7 +2117,7 @@ body.resizing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.device-title {
|
.device-title {
|
||||||
font-family: var(--font-book);
|
font-family: var(--font-heading);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: calc(24 * var(--dv-scale));
|
font-size: calc(24 * var(--dv-scale));
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
@@ -2138,7 +2147,7 @@ body.resizing {
|
|||||||
.device-body h1,
|
.device-body h1,
|
||||||
.device-body h2,
|
.device-body h2,
|
||||||
.device-body h3 {
|
.device-body h3 {
|
||||||
font-family: var(--font-book);
|
font-family: var(--font-heading);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
margin: 1.2em 0 0.5em;
|
margin: 1.2em 0 0.5em;
|
||||||
|
|||||||
@@ -29,3 +29,67 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "EB Garamond";
|
||||||
|
src: url("/fonts/EBGaramond-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 400 800;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "EB Garamond";
|
||||||
|
src: url("/fonts/EBGaramond-Italic-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 400 800;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Lora";
|
||||||
|
src: url("/fonts/Lora-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 400 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Lora";
|
||||||
|
src: url("/fonts/Lora-Italic-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 400 700;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Serif 4";
|
||||||
|
src: url("/fonts/SourceSerif4-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 200 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Serif 4";
|
||||||
|
src: url("/fonts/SourceSerif4-Italic-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 200 900;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Fraunces";
|
||||||
|
src: url("/fonts/Fraunces-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Fraunces";
|
||||||
|
src: url("/fonts/Fraunces-Italic-VF.ttf") format("truetype-variations");
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
:root {
|
:root {
|
||||||
--font-ui: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
|
--font-ui: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||||
--font-book: "Literata", Georgia, "Times New Roman", serif;
|
--font-book: "Literata", Georgia, "Times New Roman", serif;
|
||||||
|
--font-heading: "Literata", Georgia, "Times New Roman", serif;
|
||||||
|
|
||||||
--r-sm: 5px;
|
--r-sm: 5px;
|
||||||
--r-md: 8px;
|
--r-md: 8px;
|
||||||
|
|||||||
Reference in new issue
Block a user