mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Make builds of the editor and the website serve their own local fonts (#2186)
* WIP * Done? * Install fonts in CI * Use absolute path so minified inlined CSS works * Fix Bezier-rs demo fonts? * Use opsz * Revert removal of text balancer * Pull in the text balancer from our static host
This commit is contained in:
@@ -9,11 +9,13 @@ module.exports = {
|
||||
ecmaVersion: 2020,
|
||||
},
|
||||
extends: [
|
||||
// JS defaults
|
||||
"airbnb-base",
|
||||
// General Prettier defaults
|
||||
"eslint:recommended",
|
||||
"plugin:import/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/typescript",
|
||||
"prettier",
|
||||
],
|
||||
plugins: ["import", "@typescript-eslint", "prettier"],
|
||||
settings: {
|
||||
// https://github.com/import-js/eslint-plugin-import#resolvers
|
||||
"import/resolver": {
|
||||
@@ -30,7 +32,6 @@ module.exports = {
|
||||
"!.*.js",
|
||||
"!.*.ts",
|
||||
],
|
||||
plugins: ["prettier"],
|
||||
rules: {
|
||||
// Standard ESLint config
|
||||
indent: "off",
|
||||
|
||||
2
website/.gitignore
vendored
2
website/.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
node_modules/
|
||||
public/
|
||||
static/fonts/
|
||||
static/syntax-highlighting.css
|
||||
static/text-balancer.js
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
title = "Graphite"
|
||||
description = "2D raster & vector editor that melds traditional layers & tools with a modern node-based procedural workflow."
|
||||
base_url = "https://graphite.rs"
|
||||
feed_filenames = ["rss.xml"]
|
||||
|
||||
compile_sass = true
|
||||
minify_html = true
|
||||
|
||||
[markdown]
|
||||
highlight_code = true
|
||||
highlight_theme = "css"
|
||||
highlight_themes_css = [
|
||||
{ theme = "kronuz", filename = "syntax-highlighting.css" },
|
||||
]
|
||||
|
||||
[extra]
|
||||
# Put all your custom variables here
|
||||
@@ -3,12 +3,14 @@ title = "Free online vector editor & procedural design tool"
|
||||
template = "section.html"
|
||||
|
||||
[extra]
|
||||
css = ["/page/index.css", "/component/carousel.css", "/component/feature-icons.css", "/component/feature-box.css", "/component/youtube-embed.css", "/layout/balance-text.css"]
|
||||
css = ["/page/index.css", "/component/carousel.css", "/component/feature-icons.css", "/component/feature-box.css", "/component/youtube-embed.css"]
|
||||
js = ["/js/carousel.js", "/js/youtube-embed.js", "/js/video-autoplay.js"]
|
||||
linked_js = ["https://static.graphite.rs/text-balancer/text-balancer.js"]
|
||||
linked_js = []
|
||||
meta_description = "Open source free software. A vector graphics creativity suite with a clean, intuitive interface. Opens instantly (no signup) and runs locally in a browser. Exports SVG, PNG, JPG."
|
||||
+++
|
||||
|
||||
<!-- replacements::text_balancer() -->
|
||||
|
||||
<!-- ▛ LOGO ▜ -->
|
||||
<section id="logo">
|
||||
<div class="block">
|
||||
|
||||
@@ -3,7 +3,6 @@ title = "About Graphite"
|
||||
|
||||
[extra]
|
||||
css = ["/page/about.css", "/component/feature-box.css"]
|
||||
linked_css = ["https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap"]
|
||||
+++
|
||||
|
||||
<section>
|
||||
@@ -117,7 +116,7 @@ It's easy to learn and teach, yet Graphite's accessible design does not sacrific
|
||||
|
||||
<img src="https://static.graphite.rs/content/about/core-team-photo-keavon-chambers.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Keavon Chambers" />
|
||||
|
||||
## Keavon Chambers <span class="handle">(@Keavon)</span> <span class="flag" title="American">🇺🇸</span>
|
||||
## Keavon Chambers <span class="handle">(@Keavon)</span> <img src="https://static.graphite.rs/icons/flags/us.png" class="flag" title="American" />
|
||||
|
||||
***Founder, UI & product design, frontend, editor systems***
|
||||
|
||||
@@ -128,7 +127,7 @@ Keavon is a creative generalist with a love for the fusion of arts and technolog
|
||||
|
||||
<img src="https://static.graphite.rs/content/about/core-team-photo-dennis-kobert.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Dennis Kobert" />
|
||||
|
||||
## Dennis Kobert <span class="handle">(@TrueDoctor)</span> <span class="flag" title="German">🇩🇪</span>
|
||||
## Dennis Kobert <span class="handle">(@TrueDoctor)</span> <img src="https://static.graphite.rs/icons/flags/de.png" class="flag" title="German" />
|
||||
|
||||
***Graphene node engine, research, architecture***
|
||||
|
||||
@@ -143,7 +142,7 @@ Dennis is a mix between a mathematician and a mad scientist. While still enjoyin
|
||||
|
||||
<img src="https://static.graphite.rs/content/about/core-team-photo-hypercube__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Hypercube" />
|
||||
|
||||
## "Hypercube" <span class="handle">(@0Hypercube)</span> <span class="flag" title="British">🇬🇧</span>
|
||||
## "Hypercube" <span class="handle">(@0Hypercube)</span> <img src="https://static.graphite.rs/icons/flags/gb.png" class="flag" title="British" />
|
||||
|
||||
***Editor systems, nodes, tools, architecture***
|
||||
|
||||
@@ -155,7 +154,7 @@ Dennis is a mix between a mathematician and a mad scientist. While still enjoyin
|
||||
|
||||
<img src="https://static.graphite.rs/content/about/core-team-photo-adam-gerhant.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Adam Gerhant" />
|
||||
|
||||
## Adam Gerhant <span class="handle">(@pendapia)</span> <span class="flag" title="American">🇺🇸</span>
|
||||
## Adam Gerhant <span class="handle">(@pendapia)</span> <img src="https://static.graphite.rs/icons/flags/us.png" class="flag" title="American" />
|
||||
|
||||
***Editor graph tooling, node data formats***
|
||||
|
||||
|
||||
@@ -145,17 +145,21 @@ Always on the bleeding edge and built to last— Graphite is written on a robust
|
||||
<img class="atlas" style="--atlas-index: 7" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Desktop app (Windows, Mac, Linux)</span>
|
||||
</div>
|
||||
<div class="feature-icon ongoing">
|
||||
<div class="feature-icon ongoing" title="Development Ongoing">
|
||||
<img class="atlas" style="--atlas-index: 40" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Simplified main properties panel</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<div class="feature-icon ongoing" title="Development Ongoing">
|
||||
<img class="atlas" style="--atlas-index: 12" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>GPU-accelerated raster rendering</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 14" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Infinitely zoomable/panable content</span>
|
||||
<span>Infinitely zoomable/panable raster</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 21" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Select mode (marquee masking)</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 41" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
@@ -171,7 +175,7 @@ Always on the bleeding edge and built to last— Graphite is written on a robust
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 57" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Signed distance fields</span>
|
||||
<span>Signed distance field rendering</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 56" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
@@ -181,10 +185,6 @@ Always on the bleeding edge and built to last— Graphite is written on a robust
|
||||
<img class="atlas" style="--atlas-index: 52" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Command palette and context menus</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 21" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Select mode (marquee masking)</span>
|
||||
</div>
|
||||
<div class="feature-icon">
|
||||
<img class="atlas" style="--atlas-index: 53" src="https://static.graphite.rs/icons/icon-atlas-roadmap__3.png" alt="" />
|
||||
<span>Local fonts access</span>
|
||||
|
||||
194
website/install-fonts.js
Normal file
194
website/install-fonts.js
Normal file
@@ -0,0 +1,194 @@
|
||||
const fs = require("fs");
|
||||
const https = require("https");
|
||||
const path = require("path");
|
||||
|
||||
// Define basePath
|
||||
const basePath = path.resolve(__dirname);
|
||||
|
||||
// Define files to copy as [source, destination] pairs
|
||||
// Files with the same destination will be concatenated
|
||||
const FILES_TO_COPY = [
|
||||
["node_modules/@fontsource-variable/inter/opsz.css", "static/fonts/common.css"],
|
||||
["node_modules/@fontsource-variable/inter/opsz-italic.css", "static/fonts/common.css"],
|
||||
["node_modules/@fontsource/bona-nova/700.css", "static/fonts/common.css"],
|
||||
];
|
||||
|
||||
// Define directories to copy recursively as [source, destination] pairs
|
||||
const DIRECTORIES_TO_COPY = [
|
||||
["node_modules/@fontsource-variable/inter/files", "static/fonts/files"],
|
||||
["node_modules/@fontsource/bona-nova/files", "static/fonts/files"],
|
||||
];
|
||||
|
||||
// Track processed destination files and CSS content
|
||||
const processedDestinations = new Set();
|
||||
const cssDestinations = new Set();
|
||||
const allCopiedFiles = new Set();
|
||||
|
||||
// Process each file
|
||||
FILES_TO_COPY.forEach(([source, dest]) => {
|
||||
// Convert relative paths to absolute paths
|
||||
const sourcePath = path.join(basePath, source);
|
||||
const destPath = path.join(basePath, dest);
|
||||
|
||||
// Track CSS destinations for later analysis
|
||||
if (dest.endsWith(".css")) {
|
||||
cssDestinations.add(destPath);
|
||||
}
|
||||
|
||||
// Ensure destination directory exists
|
||||
const destDir = path.dirname(destPath);
|
||||
if (!fs.existsSync(destDir)) {
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
console.log(`Created directory: ${destDir}`);
|
||||
}
|
||||
|
||||
try {
|
||||
// Read source file content
|
||||
const content = fs.readFileSync(sourcePath, "utf8");
|
||||
|
||||
// Check if destination has been processed before
|
||||
if (processedDestinations.has(destPath)) {
|
||||
// Append to existing file
|
||||
fs.appendFileSync(destPath, "\n\n" + content);
|
||||
console.log(`Appended: ${sourcePath} → ${destPath}`);
|
||||
} else {
|
||||
// First time writing to this destination - copy the file
|
||||
fs.writeFileSync(destPath, content);
|
||||
processedDestinations.add(destPath);
|
||||
console.log(`Copied: ${sourcePath} → ${destPath}`);
|
||||
}
|
||||
|
||||
// Replace all occurrences of "./files" with "/fonts" in the destination file
|
||||
let destFileContent = fs.readFileSync(destPath, "utf8");
|
||||
destFileContent = destFileContent.replaceAll("./files/", "/fonts/files/");
|
||||
fs.writeFileSync(destPath, destFileContent);
|
||||
} catch (error) {
|
||||
console.error(`Error processing ${sourcePath} to ${destPath}:`, error);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// Function to recursively copy a directory
|
||||
function copyDirectoryRecursive(source, destination) {
|
||||
// Ensure destination directory exists
|
||||
if (!fs.existsSync(destination)) {
|
||||
fs.mkdirSync(destination, { recursive: true });
|
||||
console.log(`Created directory: ${destination}`);
|
||||
}
|
||||
|
||||
// Get all items in the source directory
|
||||
const items = fs.readdirSync(source);
|
||||
|
||||
// Process each item
|
||||
items.forEach((item) => {
|
||||
const sourcePath = path.join(source, item);
|
||||
const destPath = path.join(destination, item);
|
||||
|
||||
// Check if item is a directory or file
|
||||
const stats = fs.statSync(sourcePath);
|
||||
if (stats.isDirectory()) {
|
||||
// Recursively copy subdirectory
|
||||
copyDirectoryRecursive(sourcePath, destPath);
|
||||
} else {
|
||||
// Copy file and track it
|
||||
fs.copyFileSync(sourcePath, destPath);
|
||||
allCopiedFiles.add(destPath);
|
||||
console.log(`Copied: ${sourcePath} → ${destPath}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process each directory
|
||||
DIRECTORIES_TO_COPY.forEach(([source, dest]) => {
|
||||
// Convert relative paths to absolute paths
|
||||
const sourcePath = path.join(basePath, source);
|
||||
const destPath = path.join(basePath, dest);
|
||||
|
||||
try {
|
||||
copyDirectoryRecursive(sourcePath, destPath);
|
||||
console.log(`Copied directory: ${sourcePath} → ${destPath}`);
|
||||
} catch (error) {
|
||||
console.error(`Error copying directory ${sourcePath} to ${destPath}:`, error);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("All files and directories copied successfully!");
|
||||
|
||||
// Now check which of the copied files are actually referenced in CSS
|
||||
console.log("\nChecking for unused font files...");
|
||||
|
||||
// Read all CSS content and join it
|
||||
let allCssContent = "";
|
||||
cssDestinations.forEach((cssPath) => {
|
||||
try {
|
||||
const content = fs.readFileSync(cssPath, "utf8");
|
||||
allCssContent += content;
|
||||
} catch (error) {
|
||||
console.error(`Error reading CSS file ${cssPath}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// Filter files that aren't referenced in CSS
|
||||
const unusedFiles = [];
|
||||
allCopiedFiles.forEach((filePath) => {
|
||||
const fileName = path.basename(filePath);
|
||||
|
||||
// Check if the file name is mentioned in any CSS
|
||||
if (!allCssContent.includes(fileName)) {
|
||||
unusedFiles.push(filePath);
|
||||
}
|
||||
});
|
||||
|
||||
// Delete unused files
|
||||
if (unusedFiles.length > 0) {
|
||||
console.log(`Found ${unusedFiles.length} unused font files to delete:`);
|
||||
unusedFiles.forEach((filePath) => {
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
console.log(`Deleted unused file: ${filePath}`);
|
||||
} catch (error) {
|
||||
console.error(`Error deleting file ${filePath}:`, error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("No unused font files found.");
|
||||
}
|
||||
|
||||
console.log("\nFont installation complete!");
|
||||
|
||||
// Fetch and save text-balancer.js, which we don't commit to the repo so we're not version controlling dependency code
|
||||
const textBalancerUrl = "https://static.graphite.rs/text-balancer/text-balancer.js";
|
||||
const textBalancerDest = path.join(basePath, "static", "text-balancer.js");
|
||||
console.log("\nDownloading text-balancer.js...");
|
||||
https
|
||||
.get(textBalancerUrl, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
console.error(`Failed to download text-balancer.js. Status code: ${res.statusCode}`);
|
||||
res.resume();
|
||||
return;
|
||||
}
|
||||
|
||||
let data = "";
|
||||
res.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on("end", () => {
|
||||
try {
|
||||
// Ensure destination directory exists
|
||||
const destDir = path.dirname(textBalancerDest);
|
||||
if (!fs.existsSync(destDir)) {
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
console.log(`Created directory: ${destDir}`);
|
||||
}
|
||||
fs.writeFileSync(textBalancerDest, data, "utf8");
|
||||
console.log(`Downloaded and saved: ${textBalancerDest}`);
|
||||
} catch (error) {
|
||||
console.error(`Error saving text-balancer.js:`, error);
|
||||
}
|
||||
});
|
||||
})
|
||||
.on("error", (err) => {
|
||||
console.error(`Error downloading text-balancer.js:`, err);
|
||||
});
|
||||
@@ -4,9 +4,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>Bezier-rs Interactive Documentation</title>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Bona+Nova:wght@700&family=Inter:wght@500&display=swap" rel="stylesheet" />
|
||||
<link href="fonts.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>JavaScript is required</noscript>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-family: "Inter Variable", sans-serif;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
@@ -29,5 +29,6 @@ mkdir dist/libraries/bezier-rs
|
||||
cd bezier-rs-demos
|
||||
npm ci
|
||||
NODE_ENV=production npm run build
|
||||
cp ../../static/fonts/common.css dist/fonts.css
|
||||
mv dist/* ../dist/libraries/bezier-rs
|
||||
cd ..
|
||||
|
||||
1507
website/package-lock.json
generated
1507
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
"name": "graphite-website",
|
||||
"description": "Graphite's website. This npm package is for dev tooling only, such as eslint.",
|
||||
"private": true,
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/GraphiteEditor/Graphite.git"
|
||||
@@ -10,15 +9,22 @@
|
||||
"author": "Graphite Authors <contact@graphite.rs>",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://graphite.rs",
|
||||
"scripts": {
|
||||
"install-fonts": "npm ci && node install-fonts.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
||||
"@typescript-eslint/parser": "^8.7.0",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.3",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"prettier": "^3.3.3",
|
||||
"sass": "^1.78.0"
|
||||
"sass": "1.78.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "^5.2.5",
|
||||
"@fontsource/bona-nova": "^5.2.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ html,
|
||||
body {
|
||||
color: var(--color-navy);
|
||||
background: white;
|
||||
font-family: "Inter", sans-serif;
|
||||
font-family: "Inter Variable", sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
@@ -157,12 +157,12 @@ body > .page {
|
||||
background-image: url('data:image/svg+xml;utf8,\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M8,15C5.12471,9.753694 0.5,8.795225 0.5,4.736524 C0.5,-0.507473 7.468734,0 8,4.967381 C8.531266,0 15.5,-0.507473 15.5,4.736524 C15.5,8.795225 10.87529,9.753694 8,15z" fill="%23cc304f" /></svg>\
|
||||
');
|
||||
display: inline-block;
|
||||
width: 0.75em;
|
||||
height: 0.75em;
|
||||
margin-left: 0.25em;
|
||||
margin-bottom: -0.1em;
|
||||
vertical-align: baseline;
|
||||
display: inline-block;
|
||||
width: 0.75em;
|
||||
height: 0.75em;
|
||||
margin-left: 0.25em;
|
||||
margin-bottom: -0.1em;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +323,6 @@ body > .page {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
details[open] summary::before {
|
||||
@@ -436,7 +435,7 @@ h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-family: "Inter Variable", sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 800;
|
||||
display: inline-block;
|
||||
@@ -663,7 +662,7 @@ hr,
|
||||
|
||||
.arrow::after {
|
||||
content: " »";
|
||||
font-family: "Inter", sans-serif;
|
||||
font-family: "Inter Variable", sans-serif;
|
||||
}
|
||||
|
||||
.video-background {
|
||||
|
||||
@@ -38,7 +38,7 @@ pre {
|
||||
content: attr(data-lang);
|
||||
color: rgba(var(--color-seaside-rgb), 0.5);
|
||||
text-transform: lowercase;
|
||||
font-family: "Inter", sans-serif;
|
||||
font-family: "Inter Variable", sans-serif;
|
||||
font-size: 0.75em;
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
h1.feature-box-header.feature-box-header {
|
||||
&,
|
||||
& a {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-family: "Inter Variable", sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// CSS component of the JS text balancer script.
|
||||
// This must be loaded as an inline stylesheet in the head of the
|
||||
// document to avoid the possibility of a visible layout shift.
|
||||
|
||||
.balance-text {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@media (scripting: none) {
|
||||
.balance-text {
|
||||
visibility: visible !important;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (text-wrap: balance) {
|
||||
.balance-text,
|
||||
.balanced-text {
|
||||
text-align: left;
|
||||
text-wrap: balance;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,12 @@
|
||||
animation: fadeInAfterWait 2s ease-in 2s forwards;
|
||||
|
||||
@keyframes fadeInAfterWait {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +38,9 @@
|
||||
}
|
||||
|
||||
.flag {
|
||||
font-family: "Noto Color Emoji", sans-serif;
|
||||
font-style: normal;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: middle;
|
||||
font-size: 0.8em;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</section>
|
||||
{%- if not page.summary -%}
|
||||
{{ throw(message = "ARTICLE HAS NO SUMMARY! After the first paragraph (or two short ones), a `<!-- more -->` comment must be inserted in the markdown. Otherwise the blog page would be missing its preview text." | safe) }}
|
||||
{{ throw(message = "------------------------------------------------------------> ARTICLE HAS NO SUMMARY! After the first paragraph (or two short ones), a `<!-- more -->` comment must be inserted in the markdown. Otherwise the blog page would be missing its preview text." | safe) }}
|
||||
{%- endif -%}
|
||||
{%- endblock content -%}
|
||||
|
||||
|
||||
@@ -27,15 +27,19 @@
|
||||
{% block rss -%}
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="{{ get_url(path = 'blog/rss.xml', trailing_slash = false) | safe }}" />
|
||||
{%- endblock %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
{#- ======================================================================== -#}
|
||||
|
||||
{#- ON EVERY PAGE OF THE SITE: CSS AND JS TO LOAD EITHER AS A LINK OR INLINE -#}
|
||||
{#- ======================================================================== -#}
|
||||
{%- set global_linked_css = ["https://fonts.googleapis.com/css2?family=Bona+Nova:wght@700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"] -%}
|
||||
{%- set global_linked_js = [] -%}
|
||||
{%- set global_css = ["/base.css"] -%}
|
||||
{%- set global_linked_css = [] -%}
|
||||
{%- set global_js = ["/js/text-justification.js", "/js/navbar.js"] -%}
|
||||
{%- set global_css = ["/base.css", "/fonts/common.css"] -%}
|
||||
{%- set fonts_loaded = load_data(path="static/fonts/common.css", format="plain", required=false) -%}
|
||||
{%- if not fonts_loaded -%}
|
||||
{{ throw(message="------------------------------------------------------------> FONTS ARE NOT INSTALLED! Before running Zola, execute `npm run install-fonts` from the `/website` directory.") }}
|
||||
{%- endif -%}
|
||||
|
||||
{#- RETRIEVE FROM TEMPLATES AND PAGES: CSS AND JS TO LOAD EITHER AS A LINK OR INLINE -#}
|
||||
{#- ================================================================================ -#}
|
||||
@@ -89,6 +93,7 @@
|
||||
{{ load_data(path = path) | safe }}
|
||||
{{ "</" ~ "script>" | safe }}
|
||||
{%- endfor %}
|
||||
|
||||
{{- get_env(name = "INDEX_HTML_HEAD_INCLUSION", default = "") | safe }}
|
||||
</head>
|
||||
<body>
|
||||
@@ -119,8 +124,10 @@
|
||||
</header>
|
||||
<main>
|
||||
{%- filter replace(from = "<!-- replacements::blog_posts(count = 2) -->", to = replacements::blog_posts(count = 2)) -%}
|
||||
{%- filter replace(from = "<!-- replacements::text_balancer() -->", to = replacements::text_balancer()) -%}
|
||||
{%- block content -%}{%- endblock -%}
|
||||
{%- endfilter -%}
|
||||
{%- endfilter -%}
|
||||
</main>
|
||||
<footer>
|
||||
<hr />
|
||||
|
||||
@@ -12,3 +12,29 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endmacro blog_posts %}
|
||||
|
||||
{% macro text_balancer() %}
|
||||
<style>
|
||||
.balance-text {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@media (scripting: none) {
|
||||
.balance-text {
|
||||
visibility: visible !important;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (text-wrap: balance) {
|
||||
.balance-text,
|
||||
.balanced-text {
|
||||
text-align: left;
|
||||
text-wrap: balance;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
{{ load_data(path="static/text-balancer.js", format="plain") | safe }}
|
||||
</script>
|
||||
{% endmacro text_balancer %}
|
||||
|
||||
Reference in New Issue
Block a user