This commit is contained in:
42
.gitea/workflows/deploy.yml
Normal file
42
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: Build and Deploy DAVE | DMGs Site
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Cache pnpm store
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.pnpm-store
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --no-frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
run: |
|
||||||
|
rm -rf /var/www/dave-dmg/blog/*
|
||||||
|
cp -r dist/* /var/www/dave-dmg/blog/
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import { defineMarkdocConfig, component, nodes } from '@astrojs/markdoc/config';
|
import {
|
||||||
|
defineMarkdocConfig,
|
||||||
|
component,
|
||||||
|
nodes,
|
||||||
|
Markdoc,
|
||||||
|
} from '@astrojs/markdoc/config';
|
||||||
|
|
||||||
export default defineMarkdocConfig({
|
export default defineMarkdocConfig({
|
||||||
nodes: {
|
nodes: {
|
||||||
@@ -16,5 +21,70 @@ export default defineMarkdocConfig({
|
|||||||
color: { type: String },
|
color: { type: String },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Callout: {
|
||||||
|
render: component('./src/components/content/Callout.astro'),
|
||||||
|
attributes: {
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'default',
|
||||||
|
},
|
||||||
|
title: { type: String },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Figure: {
|
||||||
|
render: component('./src/components/content/Figure.astro'),
|
||||||
|
selfClosing: true,
|
||||||
|
attributes: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
alt: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
caption: { type: String },
|
||||||
|
credit: { type: String },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sidenote: {
|
||||||
|
selfClosing: true,
|
||||||
|
attributes: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
marker: {
|
||||||
|
type: String,
|
||||||
|
default: '⋄',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transform(node, config) {
|
||||||
|
const attrs = node.transformAttributes(config);
|
||||||
|
return new Markdoc.Tag('sup', {}, [
|
||||||
|
new Markdoc.Tag(
|
||||||
|
'a',
|
||||||
|
{
|
||||||
|
href: `#${attrs.id}`,
|
||||||
|
id: `ref-${attrs.id}`,
|
||||||
|
class: 'sidenote-ref',
|
||||||
|
style: `anchor-name: --note-${attrs.id}`,
|
||||||
|
},
|
||||||
|
[`[${attrs.marker}]`],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
103
src/components/content/Callout.astro
Normal file
103
src/components/content/Callout.astro
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
type: | "default" | "optional" | "example" | "spoiler";
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, title} = Astro.props;
|
||||||
|
const isSpoiler = type === 'spoiler'
|
||||||
|
---
|
||||||
|
|
||||||
|
{isSpoiler ? (
|
||||||
|
<details class={`callout spoiler`}>
|
||||||
|
<summary>
|
||||||
|
<span class="title">{title || 'Show spoiler'}</span>
|
||||||
|
<span class="badge">{type.toUpperCase()}</span>
|
||||||
|
</summary>
|
||||||
|
<div class="body">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
) : (
|
||||||
|
<div class={`callout ${type}`}>
|
||||||
|
{title && <header class="title">{title}</header>}
|
||||||
|
<span class="badge">{type.toUpperCase()}</span>
|
||||||
|
<div class="body">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.callout {
|
||||||
|
--callout-symbol: "";
|
||||||
|
|
||||||
|
@mixin my var(--space-8);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
padding: var(--space-8) var(--space-8) var(--space-8) var(--space-14);
|
||||||
|
border: var(--space-1) solid var(--color-surface-inverse);
|
||||||
|
box-shadow: var(--color-shadow);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: var(--callout-symbol);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: var(--space-10);
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--type-4);
|
||||||
|
line-height: 1.875;
|
||||||
|
color: var(--color-primary);
|
||||||
|
|
||||||
|
background: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .title {
|
||||||
|
@mixin typo_body-alt;
|
||||||
|
@mixin my var(--size-0);
|
||||||
|
|
||||||
|
font-family: var(--font-header);
|
||||||
|
font-weight: 900;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .badge {
|
||||||
|
@mixin px var(--size-4);
|
||||||
|
@mixin py var(--size-2);
|
||||||
|
@mixin typo_code;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
|
||||||
|
background: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.example {
|
||||||
|
--callout-symbol: '◆';
|
||||||
|
}
|
||||||
|
|
||||||
|
&.default {
|
||||||
|
--callout-symbol: '‽';
|
||||||
|
}
|
||||||
|
|
||||||
|
&.optional {
|
||||||
|
--callout-symbol: '★'
|
||||||
|
}
|
||||||
|
|
||||||
|
&.spoiler {
|
||||||
|
--callout-symbol: "⌧"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
63
src/components/content/Figure.astro
Normal file
63
src/components/content/Figure.astro
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
import { Image } from 'astro:assets';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
src: string;
|
||||||
|
alt: string;
|
||||||
|
caption?: string;
|
||||||
|
credit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { src, alt, caption, credit } = Astro.props;
|
||||||
|
|
||||||
|
const images = import.meta.glob<{ default: ImageMetadata }>(
|
||||||
|
'/src/content/**/*.{png,jpg,jpeg,webp,avif}',
|
||||||
|
{ eager: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const imagePath = `/src${src}`;
|
||||||
|
console.log(imagePath);
|
||||||
|
|
||||||
|
const image = images[imagePath]?.default;
|
||||||
|
---
|
||||||
|
|
||||||
|
<figure class="wrapper">
|
||||||
|
{image ? (
|
||||||
|
<Image src={image} alt={alt} loading="lazy" />
|
||||||
|
) : (
|
||||||
|
<img src={src} alt={alt} loading="lazy" />
|
||||||
|
)}
|
||||||
|
{(caption || credit) && (
|
||||||
|
<figcaption class="container">
|
||||||
|
{caption && <span class="caption">{caption}</span>}
|
||||||
|
{credit && <cite class="credit">{credit}</cite>}
|
||||||
|
</figcaption>
|
||||||
|
)}
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
@mixin border-x var(--size-6), solid, var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: var(--space-4);
|
||||||
|
background-color: var(--color-surface-inverse);
|
||||||
|
|
||||||
|
& .caption {
|
||||||
|
@mixin typo_code;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .credit {
|
||||||
|
@mixin typo_fine;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
color: var(--color-palette-stone);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
114
src/components/content/Sidenote.astro
Normal file
114
src/components/content/Sidenote.astro
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
marker: string;
|
||||||
|
type: string;
|
||||||
|
content: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {id, marker, type, content, title} = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="note" id={id} style={`position-anchor: --note-${id}; --marker: "${marker}"`}>
|
||||||
|
<div class="container content">
|
||||||
|
{title && <header class="title">{title}</header>}
|
||||||
|
<span class="badge">{type.toUpperCase()}</span>
|
||||||
|
<p class="body">{content}</p>
|
||||||
|
<footer class="backref">
|
||||||
|
<a href={`#ref-${id}`}>↩</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.note {
|
||||||
|
scroll-margin-top: calc(var(--layout-header-height) + var(--size-8));
|
||||||
|
font-size: clamp(1rem, 2.5vw, 1.25rem);
|
||||||
|
|
||||||
|
@supports (anchor-name: --test) {
|
||||||
|
@media (--bp-desktop) {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(anchor(top) - var(--size-10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
padding: var(--space-10) var(--space-8) var(--space-6) var(--space-14);
|
||||||
|
border: var(--space-1) solid var(--color-surface-inverse);
|
||||||
|
box-shadow: var(--color-shadow);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: var(--marker);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: var(--space-10);
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--type-4);
|
||||||
|
line-height: 1.875;
|
||||||
|
color: var(--color-primary);
|
||||||
|
|
||||||
|
background: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .title {
|
||||||
|
@mixin typo_body-alt;
|
||||||
|
@mixin my var(--size-0);
|
||||||
|
|
||||||
|
font-family: var(--font-header);
|
||||||
|
font-weight: 900;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .badge {
|
||||||
|
@mixin px var(--size-4);
|
||||||
|
@mixin py var(--size-2);
|
||||||
|
@mixin typo_code;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
|
||||||
|
background: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .body {
|
||||||
|
@mixin typo_body-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .backref {
|
||||||
|
@mixin mt var(--size-4);
|
||||||
|
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
& a {
|
||||||
|
@mixin pa var(--size-2);
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-color-tertiary);
|
||||||
|
transition:
|
||||||
|
color var(--motion-normal) var(--ease-out),
|
||||||
|
background var(--motion-normal) var(--ease-out);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
text-decoration: none;
|
||||||
|
background: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -57,8 +57,7 @@ const image = images[imagePath]?.default;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.caption {
|
.caption {
|
||||||
padding: var(--spacing-snug);
|
padding: var(--space-4);
|
||||||
font-family: var(--font-mono);
|
|
||||||
color: var(--color-text-inverse);
|
color: var(--color-text-inverse);
|
||||||
background-color: var(--color-surface-inverse);
|
background-color: var(--color-surface-inverse);
|
||||||
}
|
}
|
||||||
@@ -70,6 +69,6 @@ const image = images[imagePath]?.default;
|
|||||||
|
|
||||||
.caption-text,
|
.caption-text,
|
||||||
.caption-meta {
|
.caption-meta {
|
||||||
font-size: var(--typo-size-sm);
|
@mixin typo_code;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ seo:
|
|||||||
- **Foundational system** of the crucible
|
- **Foundational system** of the crucible
|
||||||
- Generates **entities** – cultures, religions, states, vessels, magical traditions – through **systematic elemental combination,** not arbitrary assignment
|
- Generates **entities** – cultures, religions, states, vessels, magical traditions – through **systematic elemental combination,** not arbitrary assignment
|
||||||
- Two interlocking element sets
|
- Two interlocking element sets
|
||||||
- **Three Primes =>** Soul, Body, Spirit; dynamic forces in tension
|
- **Three Primes** **=>** Soul, Body, Spirit; dynamic forces in tension
|
||||||
- **Five Essences =>** Earth, Fire, Water, Air, Aether; multivalent elemental character
|
- **Five Essences =>** Earth, Fire, Water, Air, Aether; multivalent elemental character
|
||||||
- **Elemental Pool Mechanic =>** add tokens from material conditions and history, tally, spend on capabilities, traits, and relationships
|
- **Elemental Pool Mechanic =>** add tokens from material conditions and history, tally, spend on capabilities, traits, and relationships
|
||||||
- **One Engine =>** elemental pool drives everything; capabilities, identity, and cultural character all flow from the same source
|
- **One Engine =>** elemental pool drives everything; capabilities, identity, and cultural character all flow from the same source
|
||||||
@@ -210,8 +210,6 @@ seo:
|
|||||||
- No – the product is neither ingredient
|
- No – the product is neither ingredient
|
||||||
{% /table %}
|
{% /table %}
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The Seven Aspects
|
## The Seven Aspects
|
||||||
|
|
||||||
- Provide additional specificity
|
- Provide additional specificity
|
||||||
@@ -243,6 +241,12 @@ seo:
|
|||||||
- Mercury
|
- Mercury
|
||||||
- Thought, consciousness; intellect, emotion, skills
|
- Thought, consciousness; intellect, emotion, skills
|
||||||
---
|
---
|
||||||
|
- *Mysteries*
|
||||||
|
- {% ElementSymbol element="mysteries" size="var(--type-3)" color="inherit" /%}
|
||||||
|
- Gold
|
||||||
|
- Sun
|
||||||
|
- Hidden, occult; magic, destiny, secrets, *residua*{% Sidenote #residua title="What are Residua?" marker="⋄" content="Residua are our fancy name for otherworldly realms – the energies that were left over when the world was created. Think the underworld, inferno, or the elemental planes." type="default" /%}
|
||||||
|
---
|
||||||
- *Society*
|
- *Society*
|
||||||
- {% ElementSymbol element="society" size="var(--type-3)" color="inherit" /%}
|
- {% ElementSymbol element="society" size="var(--type-3)" color="inherit" /%}
|
||||||
- Tin
|
- Tin
|
||||||
@@ -255,12 +259,6 @@ seo:
|
|||||||
- Venus
|
- Venus
|
||||||
- Creation, expression; artistry, rituals, craft
|
- Creation, expression; artistry, rituals, craft
|
||||||
---
|
---
|
||||||
- *Mysteries*
|
|
||||||
- {% ElementSymbol element="mysteries" size="var(--type-3)" color="inherit" /%}
|
|
||||||
- Gold
|
|
||||||
- Sun
|
|
||||||
- Hidden, occult; magic, destiny, secrets, *residua*
|
|
||||||
---
|
|
||||||
- *Forces*
|
- *Forces*
|
||||||
- {% ElementSymbol element="forces" size="var(--type-3)" color="inherit" /%}
|
- {% ElementSymbol element="forces" size="var(--type-3)" color="inherit" /%}
|
||||||
- Iron
|
- Iron
|
||||||
|
|||||||
321
src/content/articles/domain/index.mdoc
Normal file
321
src/content/articles/domain/index.mdoc
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
---
|
||||||
|
title: Domain
|
||||||
|
subtitle: What the earth provides
|
||||||
|
summary: >-
|
||||||
|
Climate, topography, biome, features, resources. The ground beneath; what
|
||||||
|
grows, what weathers, what the soil yields. The material conditions that force
|
||||||
|
civilizations into being.
|
||||||
|
cover:
|
||||||
|
showInHeader: false
|
||||||
|
publishDate: 2026-03-05T08:47:00.000Z
|
||||||
|
status: published
|
||||||
|
isFeatured: false
|
||||||
|
parent: prima-materia
|
||||||
|
tags:
|
||||||
|
- Prima Materia
|
||||||
|
- Generation
|
||||||
|
- Domain
|
||||||
|
relatedArticles: []
|
||||||
|
seo:
|
||||||
|
noIndex: false
|
||||||
|
---
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- **Map is the Starting Point;** napkin sketch or Azgaar export
|
||||||
|
- Domain = Climate + Topography + Biome + Features + Resources
|
||||||
|
- **Scale**
|
||||||
|
- Locality → Territory → Province → Region → Area → Landmass
|
||||||
|
- *Features* and *resources* are **locality-level** – a river is *here*, iron is in *this* valley
|
||||||
|
- *Resources* = **abundance,** not exclusive access; most common material is available to anyone; the list marks what a locality has in surplus
|
||||||
|
- **Climate =>** 10 types; simplified Köppen-Geiger; temperature, precipitation, characteristics
|
||||||
|
- **Topography =>** 6 types; elevation and terrain character
|
||||||
|
- **Biome =>** 10 types; dominant vegetation;
|
||||||
|
- **Features**{% Sidenote #features-resources title="Relevance of features & resources" marker="⋄" content="Features and resources are only relevant on the locality level" type="optional" /%} **=>** 10 types; rivers, coasts, volcanoes, passes, settlements; affect availability tables downstream
|
||||||
|
- **Resources =>** 75 entries; what the land yields in abundance
|
||||||
|
- Tables follow below; elemental association appear later when they become mechanically relevant
|
||||||
|
|
||||||
|
## Procedure
|
||||||
|
|
||||||
|
1. **Pick your area or region** – the broad stroke; a continent's western coast, a mountain range, a river basin
|
||||||
|
1. **Generate localities within it** – each locality gets its own Climate, Topography, Biome
|
||||||
|
- For those wanting a more random approach, the following [table](https://docs.google.com/spreadsheets/d/16d9MUMFMqbUXkL6bOHN3sbFSvQiR0K3yta--RiW4Ldw/edit?usp=sharing) can be used to generate biomes based on Climate, Topography, and Features
|
||||||
|
1. **Assign features** – rivers, coasts, passes, settlements
|
||||||
|
1. **Roll for resources** – each locality has a **1-in-10 chance** to generate a resource
|
||||||
|
1. **Read the dominant pattern** – the Climate, Topography, and Biome that appear most across localities become the domain signature for the culture living there
|
||||||
|
{% Callout type="example" title="Gwayth Teyd" %}
|
||||||
|
Looking at the map for my »Chainbreaker« setting, I decide to start with the area on the western coast of Khimbria – my northern continent which is dominated by temperate rainforest. Knowing that I want to the dominant culture to be inspired by the britannic tribes of the antiquity, I decide to call it: the Gwayth Ted.
|
||||||
|
{% /Callout %}
|
||||||
|
|
||||||
|
## Climate
|
||||||
|
|
||||||
|
- Borrowed and simplified from Köppen-Geiger climate classification
|
||||||
|
- Ten types covering the range of conditions your world might produce
|
||||||
|
|
||||||
|
{% table %}
|
||||||
|
- Climate
|
||||||
|
- Temp.
|
||||||
|
- Precipiation
|
||||||
|
- K-G
|
||||||
|
- Characteristics
|
||||||
|
---
|
||||||
|
- *Artic*
|
||||||
|
- Extremely cold winters (< -30°C); cool summers (<10°C)
|
||||||
|
- Very low; < 300 mm/year
|
||||||
|
- ET, EF
|
||||||
|
- Cold year-round; mostly snow; permafrost
|
||||||
|
---
|
||||||
|
- *Arid*
|
||||||
|
- Hot summers (>30°C) or cold winters (<0°C)
|
||||||
|
- Very low; < 250mm/year
|
||||||
|
- BWh, BWk
|
||||||
|
- True desert; hot (BWh) or cold (BWk)
|
||||||
|
---
|
||||||
|
- *Continental*
|
||||||
|
- Cold winters (<0°C); hot summers (>25°C)
|
||||||
|
- Moderate; 500 – 1000 mm/year
|
||||||
|
- Dfa, Dfb, Dwa, Dwb
|
||||||
|
- Sharp seasonal swing; summer rainfall
|
||||||
|
---
|
||||||
|
- *Mediterranean*
|
||||||
|
- Mild winters (0 – 10°C); hot summers (>25°C)
|
||||||
|
- Moderate; 500–900mm/year; dry summers
|
||||||
|
- Csa, Csb
|
||||||
|
- Hot dry summers; wet winters
|
||||||
|
---
|
||||||
|
- *Monsoon*
|
||||||
|
- Warm year-round (>20°C); mild seasonal variation
|
||||||
|
- Very high seasonal; 1500–3000+mm/year in wet seasonal
|
||||||
|
- Am
|
||||||
|
- Extreme wet/dry cycle; flooding defines calendar
|
||||||
|
---
|
||||||
|
- *Oceanic*
|
||||||
|
- Mild winters (0–10°C); cool summers (<25°C)
|
||||||
|
- High; > 800mm/year
|
||||||
|
- Cfb, Cfc
|
||||||
|
- Mild year-round; contant rainfall; grey skies
|
||||||
|
---
|
||||||
|
- *Semiarid*
|
||||||
|
- Cold winters (often <0°C); hot summers (>25°C)
|
||||||
|
- Low to moderate; 250mm/year
|
||||||
|
- BSh, BSk
|
||||||
|
- Steppe; drier than grassland, wetter than desert
|
||||||
|
---
|
||||||
|
- *Subarctic*
|
||||||
|
- Very cold winters (< -20°C); short summers (<15°C)
|
||||||
|
- Low to moderate; 300–600mm/year
|
||||||
|
- Dfc, Dfd, Dwc, Dwd
|
||||||
|
- Brutal winters; brief growing season
|
||||||
|
---
|
||||||
|
- *Subtropical*
|
||||||
|
- Mild winters (5–10°C); hot summers (>22°C)
|
||||||
|
- Moderate to high; 600-1200mm/year
|
||||||
|
- Cfa, Cwa
|
||||||
|
- Humid heat; mild winters; even rainfall
|
||||||
|
---
|
||||||
|
- *Tropical*
|
||||||
|
- Warm year-round; avg > 18°C
|
||||||
|
- High; > 1500mm/year
|
||||||
|
- Af, Aw/As
|
||||||
|
- Constant warmth; heavy rain; Aw/As have dry season
|
||||||
|
{% /table %}
|
||||||
|
|
||||||
|
## Topography
|
||||||
|
|
||||||
|
- Lay of the land
|
||||||
|
- Elevation shapes what grows, who lives here, and how hard it is to move
|
||||||
|
|
||||||
|
{% table %}
|
||||||
|
- Topography
|
||||||
|
- Elevation
|
||||||
|
- Characteristics
|
||||||
|
---
|
||||||
|
- *Lowlands*
|
||||||
|
- Below sea level to 50 m
|
||||||
|
- Deltas, polders, fens, tidal flats; often waterlogged or reclaimed; fertile but floodprone
|
||||||
|
---
|
||||||
|
- *Plains*
|
||||||
|
- 50–300 m
|
||||||
|
- Open steppe, prairie, farmland; wide horizons; wind-exposed; good for agriculture and cavalry
|
||||||
|
---
|
||||||
|
- *Hills*
|
||||||
|
- 300–800 m
|
||||||
|
- Rolling terrain; mixed land use; defensible ridgelines; broken sightlines
|
||||||
|
---
|
||||||
|
- *Highlands*
|
||||||
|
- 800 – 1500 m (flat)
|
||||||
|
- Elevated plateaus; wind-scoured; isolated; natural fortress country
|
||||||
|
---
|
||||||
|
- *Mountains*
|
||||||
|
- 1500+ m
|
||||||
|
- Peaks, passes, and valleys; vertical climate zones; barriers to movement; mineral wealth
|
||||||
|
---
|
||||||
|
- *Badlands*
|
||||||
|
- Variable
|
||||||
|
- Eroded, broken terrain; gullies, mesas, exposed rock; poor soil; difficult to settle, easy to hide in
|
||||||
|
{% /table %}
|
||||||
|
|
||||||
|
## Biome
|
||||||
|
|
||||||
|
- Dominant Vegetation
|
||||||
|
- What grows tells you what lives
|
||||||
|
|
||||||
|
{% table %}
|
||||||
|
- Biome
|
||||||
|
- Description
|
||||||
|
---
|
||||||
|
- *Barren*
|
||||||
|
- Salt flats, volacanic waste, eroded badlands; no significant vegetation; exposed rock and mineral deposits
|
||||||
|
---
|
||||||
|
- *Tundra*
|
||||||
|
- Permafrost beneath low scrub, mosses, lichens; treeless; wind-exposed; brief growing season
|
||||||
|
---
|
||||||
|
- *Shrublands*
|
||||||
|
- Dry-adapted woody scrub; charparral, maquis, fynbos; fire-prone, wind-scoured, quick to regenerate
|
||||||
|
---
|
||||||
|
- *Grasslands*
|
||||||
|
- Open steppe, prairie, pampas; deep fertile soil beneath tall grasses; few trees, wide horizons
|
||||||
|
---
|
||||||
|
- *Savanna*
|
||||||
|
- Tropical or subtropical grassland with scattered trees; distinct wet and dry seasons; periodic burns
|
||||||
|
---
|
||||||
|
- *Wetlands*
|
||||||
|
- Bogs, marshes, fens, swamps; waterlogged ground; dense reed bets, peat deposists; high biodiversity
|
||||||
|
---
|
||||||
|
- *Woods*
|
||||||
|
- Open canopy with clearings; light penetrates to ground level; mixed undergrowth; edge habitat
|
||||||
|
---
|
||||||
|
- *Forest*
|
||||||
|
- Dense temperate woodland; heavy canopy; deep leaf litter, large hardoods; oak, beech, maple
|
||||||
|
---
|
||||||
|
- *Rainforest*
|
||||||
|
- Multi-layered tropical canopy; constant moisture; maximum biodiversity; epiphytes, vines, dense undergrowth
|
||||||
|
---
|
||||||
|
- *Taiga*
|
||||||
|
- Boreal connifer forest; spruce, pine, larch over muskeg and frozen ground;
|
||||||
|
{% /table %}
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Geographical features at the locality level
|
||||||
|
{% table %}
|
||||||
|
- Feature
|
||||||
|
- Description
|
||||||
|
---
|
||||||
|
- *River*
|
||||||
|
- Flowing freshwater; trade artery, irrigation source, natural boundary; floods deposit fertile silt
|
||||||
|
---
|
||||||
|
- *Coast*
|
||||||
|
- Land meets sea; harbors, tidal flats, salt marshes; access to maritime trade and naval power
|
||||||
|
---
|
||||||
|
- *Lake*
|
||||||
|
- Enclosed body of water; fishery, freshwater reserve, natural landmark; still water hides depth
|
||||||
|
---
|
||||||
|
- *Floodplain*
|
||||||
|
- Low ground beside rivers; seasonally inundated; rich alluvial soik; high yield, high risk
|
||||||
|
---
|
||||||
|
- *Oasis*
|
||||||
|
- Isolated water source in arid terrain; date palms, wells, caravan stops; life surrounded by nothing
|
||||||
|
---
|
||||||
|
- *Volcano*
|
||||||
|
- Active or dormant; volcanic soil is fertile, eruptions devastating; obsidian, sulfur, hot spring nearby
|
||||||
|
---
|
||||||
|
- *Island*
|
||||||
|
- Land surrounded by water; isolation breeds distinctiveness; defensible, resource-limited, reade-dependent
|
||||||
|
---
|
||||||
|
- *Mountain Pass*
|
||||||
|
- Navigable route through high terrain; chokepoint for trade and invasion; whoever holds it controls movement
|
||||||
|
---
|
||||||
|
- *Town*
|
||||||
|
- Permanent settlement; market, craft production, local administration; polulation 1000–5000
|
||||||
|
---
|
||||||
|
- *City*
|
||||||
|
- Major settlement; walls, institutions, dense population, surplus extraction, specialization, political gravity; population 5000+
|
||||||
|
{% /table %}
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
- What the land provides
|
||||||
|
- Each locality has a **1-in-10 chance** to generate a resource
|
||||||
|
- Mark **abundance** – a defining surplus worth trading or fighting over
|
||||||
|
- Resources are used in the *Conjuction* state
|
||||||
|
- Use the following [table](https://docs.google.com/spreadsheets/d/1bUUr5F93SrQ4fZm7ONf2YTudJ-1lnbyKiFHbtLgMgrY/edit?usp=sharing) to roll for the resource of a locality
|
||||||
|
|
||||||
|
| Resources | Description |
|
||||||
|
|--------------------|-------------------------------------------------------------------------------------------------------|
|
||||||
|
| Stone | Limestone, granite, sandstone, slate; ubiquitous building and toolmaking material |
|
||||||
|
| Base Metals | Copper, tin, lead, zinc; smelted from ore into everyday tools, vessels, fittings |
|
||||||
|
| Vegetables | Root crops, legumes, leafy greens; garden staples grown in moist soil |
|
||||||
|
| Fruits | Orchard and vine crops; apples, figs, pomegranates, pears; wind-pollinated, sun-ripened |
|
||||||
|
| Staple Grains | Wheat, barley, millet, sorghum; the caloric foundation of settled civilization |
|
||||||
|
| Clay | Shaped wet, hardened by kiln; pottery, brick, tile, ceramic |
|
||||||
|
| Alchemical Earths | Calcium oxide, potash, natron, vitriol; reactive mineral substances used in processing and craft |
|
||||||
|
| Fish | River and coastal catch; preserved by salt, smoke, or drying |
|
||||||
|
| Hunting Companions | Hawks, falcons, hounds; trained animals used in pursuit and killing of game |
|
||||||
|
| Resins | Hardened tree sap; pine tar, frankincense, myrrh; sealant, adhesive, incense |
|
||||||
|
| Salt | Mineral preservative and seasoning; harvested from sea, spring, or mine |
|
||||||
|
| Oilseeds | Sesame, flax, rapeseed; pressed and heated into oil for lamps, cooking, and craft |
|
||||||
|
| Furs | Beaver, rabbit, fox pelts; insulation, clothing, and high-volume trade commodity |
|
||||||
|
| Livestock | Cattle, sheep, goats, pigs; kept for meat, milk, wool, leather, traction |
|
||||||
|
| Reeds | Marsh grasses; used for weaving, thatching, basket-making, matting |
|
||||||
|
| Hemp | Coarse fiber plant; rope, sailcloth, sacking, rough textile |
|
||||||
|
| Softwoods | Pine, fir, spruce; fast-growing construction timber and primary fuel wood |
|
||||||
|
| Dye Plants | Indigo, madder, weld; plants processed into colorfast textile dyes |
|
||||||
|
| Flax | Fine fiber plant; retted and spun into linen; light, breathable fabric and sailcloth |
|
||||||
|
| Aromatic Herbs | Lavender, rosemary, sage, thyme; culinary and ritual aromatics |
|
||||||
|
| Woad | Leaf-derived blue pigment; body paint, textile dye; labor-intensive extraction |
|
||||||
|
| Psychoactives | Mushrooms, ergot, datura, cannabis; mind-altering substances used in ritual and medicine |
|
||||||
|
| Honey | Bee product; sweetener, preservative, fermented into mead; harvested from wild or kept hives |
|
||||||
|
| Wild Game | Deer, boar, hare, game birds; hunted in open country and woodland |
|
||||||
|
| Earth Pigments | Ochre, umber, sienna; mineral pigments ground for paint, body decoration, and burial rites |
|
||||||
|
| Seals | Marine mammals hunted for blubber, oil, meat, and heavy pelts; cold-water resource |
|
||||||
|
| Iron | Smelted from bog or mountain ore; tools, weapons, structural hardware; the essential metal |
|
||||||
|
| Olives | Long-lived orchard tree; fruit pressed into oil for cooking, lighting, preservation, and anointing |
|
||||||
|
| Hardwoods | Oak, ash, elm, walnut; dense timber for shipbuilding, furniture, tools, construction |
|
||||||
|
| Jade | Nephrite or jadeite; dense, hard stone worked into blades, ornaments, and ritual objects |
|
||||||
|
| Dates | Desert palm fruit; high-calorie staple of arid regions; dried for storage and transport |
|
||||||
|
| Artisan Stone | Alabaster, marble, pumice, soapstone; decorative and sculptural stone shaped by cutting and polishing |
|
||||||
|
| Dyes | Processed colorants; cochineal, kermes, woad extract; requiring boiling, mordanting, vat fermentation |
|
||||||
|
| Bamboo | Fast-growing giant grass; hollow, light, strong; construction, scaffolding, weapons, tools |
|
||||||
|
| Obsidian | Volcanic glass; fractures to razor edges; blades, arrowheads, mirrors |
|
||||||
|
| Rice | Grain cultivated in flooded paddies; requires irrigation infrastructure and water management |
|
||||||
|
| Citrus | Lemons, oranges, limes; juicy, acidic fruit; prevents scurvy, flavors food, preserves |
|
||||||
|
| Papyrus | Aquatic reed processed into writing material; stripped, layered, pressed, dried |
|
||||||
|
| Grapes | Vine fruit; eaten fresh, dried as raisins, crushed and fermented into wine and vinegar |
|
||||||
|
| Medicinal Herbs | Healing plants; prepared as poultice, tincture, tea, or salve; specialized knowledge required |
|
||||||
|
| Special Livestock | Camels, llamas, yaks, reindeer; animals adapted to extreme terrain; transport, milk, fiber, meat |
|
||||||
|
| Cotton | Tropical fiber plant; ginned, carded, spun into soft, dyeable textile |
|
||||||
|
| Pelts | Luxury furs; ermine, sable, marten; high-value status goods moving through long-distance trade |
|
||||||
|
| Horses | Riding, draft, and pack animal; cavalry, courier, transport; requires pasture and fodder |
|
||||||
|
| Silk | Caterpillar cocoon fiber; reeled, twisted, woven into extremely fine, strong fabric |
|
||||||
|
| Maize | Corn; high-yield grain requiring processing (nixtamalization); flour, porridge, flatbread |
|
||||||
|
| Yew | Slow-growing toxic hardwood; exceptional bow-stave timber; durable, flexible, poisonous |
|
||||||
|
| Tea | Dried leaf steeped in hot water; stimulant beverage; requires specific highland growing conditions |
|
||||||
|
| Ivory (marine) | Walrus and narwhal tusk; carved into fine objects, jewelry, inlay; rare and highly portable |
|
||||||
|
| Amber | Fossilized tree resin; translucent, sometimes containing preserved insects; ornamental and trade good |
|
||||||
|
| Porphyry | Dense purple-red igneous stone; extremely hard to quarry and work; imperial building material |
|
||||||
|
| Bitumen | Natural tar and petroleum seeps; waterproofing, adhesive, fuel, caulking |
|
||||||
|
| Murex | Sea snail harvested for purple dye; thousands of shells yield grams of pigment |
|
||||||
|
| Saffron | Crocus flower stigma; harvested by hand, three threads per flower; dye, spice, medicine |
|
||||||
|
| Kaolin | Crocus flower stigma; harvested by hand, three threads per flower; dye, spice, medicine |
|
||||||
|
| War Beasts | Elephants, rhinoceri, or similar; large animals trained for combat; living siege weapons |
|
||||||
|
| Precious Stones | Diamonds, rubies, sapphires, emeralds; formed under extreme pressure and heat; cut and polished |
|
||||||
|
| Arsenic | Realgar, orpiment; toxic mineral compounds; pigment, poison, glass-making additive |
|
||||||
|
| Sulfur | Yellow mineral from volcanic deposits; combustible; used in fumigation, medicine, fire-weapons |
|
||||||
|
| Cinnabar | Mercury sulfide ore; bright vermillion pigment; toxic; source of liquid mercury |
|
||||||
|
| Sugar | Cane or beet juice boiled and crystallized; requires heavy irrigation and intensive labor |
|
||||||
|
| Spices | Pepper, cinnamon, clove, nutmeg; dried seeds, bark, and buds; flavor, preservation, medicine |
|
||||||
|
| Pearls | Formed inside oysters and mussels; harvested by diving; gems requiring no cutting or polishing |
|
||||||
|
| Precious Metals | Gold and silver; panned, mined, smelted; currency, jewelry, gilding, ritual objects |
|
||||||
|
| Silphium | Extinct or near-extinct plant; medicinal, contraceptive, culinary; once worth its weight in coin |
|
||||||
|
| Steppe Horses | Superior war and riding breeds from harsh grassland; exceptional endurance and hardiness |
|
||||||
|
| Coffee | Roasted bean brewed into stimulant drink; requires specific tropical highland growing conditions |
|
||||||
|
| Whales | Hunted for oil, bone, baleen, ambergris; deep-ocean pursuit requiring specialized vessels |
|
||||||
|
| Rare Woods | Ebony, lignum vitae, purpleheart, sandalwood; dense exotic timber from distant sources |
|
||||||
|
| Cedar | Aromatic rot-resistant softwood; temple and ship timber; naturally insect-repellent |
|
||||||
|
| Cocoa | Tree pod containing bitter seeds; roasted and ground into paste or drink; requires tropical lowlands |
|
||||||
|
| Incense | Frankincense, myrrh, and similar gum resins; burned to produce aromatic smoke |
|
||||||
|
| Lapis Lazuli | Deep blue metamorphic stone; ground into ultramarine pigment; jewelry, inlay, decoration |
|
||||||
|
| Coca | Shrub leaf chewed or brewed as stimulant; suppresses hunger and altitude sickness; highland crop |
|
||||||
|
| Poppy | Flower producing opium latex; powerful painkiller and sedative; highly addictive |
|
||||||
|
|
||||||
|
{% Callout type="example" title="Gwayth Teyd Part II" %}
|
||||||
|
After having generated the localities of the Gwayth Teyd, I discover that most of its localities have a *Oceanic climate* while the terrain is mostly *Lowlands* with a *Forest* vegetation. The Gwayth Teyd also features a surpsingly high number of *Silk* and *Dye* resources.
|
||||||
|
{% /Callout %}
|
||||||
19
src/content/articles/kindred/index.mdoc
Normal file
19
src/content/articles/kindred/index.mdoc
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
title: Kindred
|
||||||
|
subtitle: What the Body is
|
||||||
|
summary: >-
|
||||||
|
Species-level biology. The body you're born in, not the culture you're born
|
||||||
|
into. Humans are the baseline; everything else carries elemental weight.
|
||||||
|
cover:
|
||||||
|
showInHeader: false
|
||||||
|
publishDate: 2026-03-05T08:51:00.000Z
|
||||||
|
status: draft
|
||||||
|
isFeatured: false
|
||||||
|
parent: prima-materia
|
||||||
|
tags:
|
||||||
|
- Prima Materia
|
||||||
|
- Generator
|
||||||
|
relatedArticles: []
|
||||||
|
seo:
|
||||||
|
noIndex: false
|
||||||
|
---
|
||||||
13
src/content/articles/meta/index.mdoc
Normal file
13
src/content/articles/meta/index.mdoc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: The Metatron
|
||||||
|
summary: Where meta stuff is presented
|
||||||
|
cover:
|
||||||
|
showInHeader: false
|
||||||
|
publishDate: 2026-03-04T23:45:00.000Z
|
||||||
|
status: published
|
||||||
|
isFeatured: false
|
||||||
|
tags: []
|
||||||
|
relatedArticles: []
|
||||||
|
seo:
|
||||||
|
noIndex: false
|
||||||
|
---
|
||||||
@@ -11,7 +11,22 @@ parent: the-crucible
|
|||||||
tags:
|
tags:
|
||||||
- Crucible Stage
|
- Crucible Stage
|
||||||
- Generation
|
- Generation
|
||||||
|
- Material Conditions
|
||||||
|
- Domain
|
||||||
|
- Kindred
|
||||||
relatedArticles: []
|
relatedArticles: []
|
||||||
seo:
|
seo:
|
||||||
noIndex: false
|
noIndex: false
|
||||||
---
|
---
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- **Stage 1** of the Crucible; tangible foundation
|
||||||
|
- Everything built later – kin, faith, magic, states – rests on what exists here
|
||||||
|
- The physical world before culture touches it
|
||||||
|
- Two halves:
|
||||||
|
- [**Domain:**](/the-crucible/prima-materia/domain) the land; climate, topography, biome, features, resources
|
||||||
|
- [**Kindred:**](/the-crucible/prima-materia/kindred) the biology; species-level bodies, physical traits, innate abilities
|
||||||
|
- Neither half generates *Culture* – that's **Calcination**
|
||||||
|
- *Prima Materia* generates **Conditions;** the material constraints culture must respond to
|
||||||
|
- Different land, different problems; different problems, different solutions; different solutions, different civilisations
|
||||||
|
- The land does not care what you believe; the body does not care what you build
|
||||||
|
|||||||
BIN
src/fonts/JuliaMono/JuliaMono-Black.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-Black.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-Bold.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-Bold.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-BoldLatin.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-BoldLatin.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-ExtraBold.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-ExtraBold.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-ExtraBoldItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-ExtraBoldItalic.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-Light.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-Light.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-LightItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-LightItalic.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-Medium.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-Medium.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-Regular.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-Regular.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-RegularLatin.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-RegularLatin.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-SemiBold.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-SemiBold.woff2
Normal file
Binary file not shown.
BIN
src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2
Normal file
BIN
src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2
Normal file
Binary file not shown.
39
src/keystatic/components/callout.ts
Normal file
39
src/keystatic/components/callout.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { wrapper } from '@keystatic/core/content-components';
|
||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
import { megaphoneIcon } from '@keystar/ui/icon/icons/megaphoneIcon';
|
||||||
|
|
||||||
|
const calloutComponent = {
|
||||||
|
Callout: wrapper({
|
||||||
|
label: 'Callout',
|
||||||
|
icon: megaphoneIcon,
|
||||||
|
schema: {
|
||||||
|
type: fields.select({
|
||||||
|
label: 'Type',
|
||||||
|
defaultValue: 'default',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Default',
|
||||||
|
value: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Optional',
|
||||||
|
value: 'optional',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Example',
|
||||||
|
value: 'example',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Spoiler',
|
||||||
|
value: 'spoiler',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
title: fields.text({
|
||||||
|
label: 'Title',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default calloutComponent;
|
||||||
35
src/keystatic/components/figure.ts
Normal file
35
src/keystatic/components/figure.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { imageIcon } from '@keystar/ui/icon/icons/imageIcon';
|
||||||
|
import { block } from '@keystatic/core/content-components';
|
||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
|
||||||
|
const figureComponent = {
|
||||||
|
Figure: block({
|
||||||
|
label: 'Figure',
|
||||||
|
icon: imageIcon,
|
||||||
|
schema: {
|
||||||
|
src: fields.image({
|
||||||
|
label: 'Image',
|
||||||
|
directory: 'src/content/articles',
|
||||||
|
publicPath: '/content/articles',
|
||||||
|
}),
|
||||||
|
alt: fields.text({
|
||||||
|
label: 'Alt Text',
|
||||||
|
validation: {
|
||||||
|
length: {
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
caption: fields.text({
|
||||||
|
label: 'Caption',
|
||||||
|
multiline: true,
|
||||||
|
}),
|
||||||
|
credit: fields.text({
|
||||||
|
label: 'Credit/Attribution',
|
||||||
|
description: 'Photographer, artist, or source',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default figureComponent;
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
import elementComponent from './element.ts';
|
import elementComponent from './element.ts';
|
||||||
|
import calloutComponent from './callout.ts';
|
||||||
|
import sidenoteComponent from './sidenote.ts';
|
||||||
|
import figureComponent from './figure.ts';
|
||||||
|
|
||||||
export const generalComponents = {
|
export const generalComponents = {
|
||||||
...elementComponent,
|
...elementComponent,
|
||||||
|
...calloutComponent,
|
||||||
|
...sidenoteComponent,
|
||||||
|
...figureComponent,
|
||||||
};
|
};
|
||||||
|
|||||||
50
src/keystatic/components/sidenote.ts
Normal file
50
src/keystatic/components/sidenote.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { inline } from '@keystatic/core/content-components';
|
||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
import { panelRightDashedIcon } from '@keystar/ui/icon/icons/panelRightDashedIcon';
|
||||||
|
|
||||||
|
const sidenoteComponents = {
|
||||||
|
Sidenote: inline({
|
||||||
|
label: 'Sidenote',
|
||||||
|
icon: panelRightDashedIcon,
|
||||||
|
schema: {
|
||||||
|
id: fields.text({
|
||||||
|
label: 'ID',
|
||||||
|
validation: {
|
||||||
|
isRequired: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
title: fields.text({
|
||||||
|
label: 'Title',
|
||||||
|
}),
|
||||||
|
marker: fields.text({
|
||||||
|
label: 'Marker',
|
||||||
|
defaultValue: '⋄',
|
||||||
|
}),
|
||||||
|
content: fields.text({
|
||||||
|
label: 'Body',
|
||||||
|
multiline: true,
|
||||||
|
validation: { isRequired: true },
|
||||||
|
}),
|
||||||
|
type: fields.select({
|
||||||
|
label: 'Type',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 'default',
|
||||||
|
label: 'Default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'example',
|
||||||
|
label: 'Example',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'optional',
|
||||||
|
label: 'Optional',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultValue: 'default',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sidenoteComponents;
|
||||||
@@ -5,6 +5,8 @@ import Base from '@layouts/Base.astro';
|
|||||||
import Header from '@compontents/layout/PostHeader/index.astro';
|
import Header from '@compontents/layout/PostHeader/index.astro';
|
||||||
import { getBreadcrumbs } from '@lib/utils/paths';
|
import { getBreadcrumbs } from '@lib/utils/paths';
|
||||||
import { toMilitaryDTG } from '@lib/utils/date';
|
import { toMilitaryDTG } from '@lib/utils/date';
|
||||||
|
import { extractSidenotes } from '@lib/utils/extractors';
|
||||||
|
import Sidenote from '@compontents/content/Sidenote.astro';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: CollectionEntry<'articles'> | CollectionEntry<'elements'>;
|
entry: CollectionEntry<'articles'> | CollectionEntry<'elements'>;
|
||||||
@@ -15,7 +17,8 @@ const { entry, collectionName } = Astro.props;
|
|||||||
const { Content } = await render(entry);
|
const { Content } = await render(entry);
|
||||||
const { title, summary, subtitle, updateDate, publishDate, tags, seo } =
|
const { title, summary, subtitle, updateDate, publishDate, tags, seo } =
|
||||||
entry.data;
|
entry.data;
|
||||||
const hasMargin = Astro.slots.has('margin')
|
const sidenotes = entry.body ? extractSidenotes(entry.body) : [];
|
||||||
|
const hasMargin = Astro.slots.has('margin') || !!sidenotes;
|
||||||
|
|
||||||
const breadcrumbs = await getBreadcrumbs(
|
const breadcrumbs = await getBreadcrumbs(
|
||||||
entry.data.parent ?? null,
|
entry.data.parent ?? null,
|
||||||
@@ -57,6 +60,7 @@ const headerCover =
|
|||||||
</div>
|
</div>
|
||||||
{hasMargin &&(
|
{hasMargin &&(
|
||||||
<aside class="margin">
|
<aside class="margin">
|
||||||
|
{sidenotes.map(note => <Sidenote {...note} />)}
|
||||||
<slot name="margin" />
|
<slot name="margin" />
|
||||||
</aside>
|
</aside>
|
||||||
)}
|
)}
|
||||||
@@ -69,8 +73,9 @@ const headerCover =
|
|||||||
.frame {
|
.frame {
|
||||||
@mixin layout-wrapper;
|
@mixin layout-wrapper;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
@media (--bp-desktop) {
|
@media (--bp-desktop) {
|
||||||
position: relative;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: var(--layout-content-width) var(--layout-margin-width);
|
grid-template-columns: var(--layout-content-width) var(--layout-margin-width);
|
||||||
gap: var(--layout-gutter);
|
gap: var(--layout-gutter);
|
||||||
@@ -82,7 +87,6 @@ const headerCover =
|
|||||||
}
|
}
|
||||||
|
|
||||||
.margin {
|
.margin {
|
||||||
position: relative;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
||||||
|
|
||||||
export type ContentType = 'article' | 'page' | 'element';
|
|
||||||
|
|
||||||
export interface ResolvedEntry {
|
|
||||||
type: ContentType;
|
|
||||||
path: string;
|
|
||||||
entry:
|
|
||||||
| CollectionEntry<'pages'>
|
|
||||||
| CollectionEntry<'articles'>
|
|
||||||
| CollectionEntry<'elements'>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildPath(
|
|
||||||
slug: string,
|
|
||||||
parentSlug: string | null,
|
|
||||||
articles: CollectionEntry<'articles'>[],
|
|
||||||
visited: Set<string> = new Set(),
|
|
||||||
): string {
|
|
||||||
if (!parentSlug) return slug;
|
|
||||||
if (visited.has(parentSlug)) {
|
|
||||||
console.warn(`Circular reference detected at ${parentSlug}`);
|
|
||||||
return slug;
|
|
||||||
}
|
|
||||||
visited.add(parentSlug);
|
|
||||||
|
|
||||||
const parent = articles.find((a) => a.slug === parentSlug);
|
|
||||||
if (!parent) return slug;
|
|
||||||
|
|
||||||
const parentPath = buildPath(
|
|
||||||
parent.slug,
|
|
||||||
parent.data.parent ?? null,
|
|
||||||
articles,
|
|
||||||
visited,
|
|
||||||
);
|
|
||||||
|
|
||||||
return `${parentPath}/${slug}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveArticles(
|
|
||||||
articles: CollectionEntry<'articles'>[],
|
|
||||||
): ResolvedEntry[] {
|
|
||||||
return articles
|
|
||||||
.filter((entry) => entry.data.status === 'published')
|
|
||||||
.map((entry) => ({
|
|
||||||
type: 'article' as const,
|
|
||||||
path: buildPath(entry.slug, entry.data.parent ?? null, articles),
|
|
||||||
entry,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GlobEntry {
|
|
||||||
id: string;
|
|
||||||
data: { status: string; parent?: string | null };
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveGlobCollection(
|
|
||||||
collection: GlobEntry[],
|
|
||||||
type: ContentType,
|
|
||||||
articles: CollectionEntry<'articles'>[],
|
|
||||||
): { type: ContentType; path: string; entry: GlobEntry }[] {
|
|
||||||
return collection
|
|
||||||
.filter((entry) => entry.data.status === 'published')
|
|
||||||
.map((entry) => ({
|
|
||||||
type,
|
|
||||||
path: buildPath(entry.id, entry.data.parent ?? null, articles),
|
|
||||||
entry,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function resolveAllPaths(): Promise<ResolvedEntry[]> {
|
|
||||||
const articles = await getCollection('articles');
|
|
||||||
const elements = await getCollection('elements');
|
|
||||||
const pages = await getCollection('pages');
|
|
||||||
|
|
||||||
const resolvedPages: ResolvedEntry[] = pages.map((page) => ({
|
|
||||||
type: 'page' as const,
|
|
||||||
path: `static/${page.slug}`,
|
|
||||||
entry: page,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return [
|
|
||||||
...resolveArticles(articles),
|
|
||||||
...resolveGlobCollection(elements, 'element', articles),
|
|
||||||
...resolvedPages,
|
|
||||||
] as ResolvedEntry[];
|
|
||||||
}
|
|
||||||
@@ -41,3 +41,13 @@ export interface PaletteEntry {
|
|||||||
parent: string | null;
|
parent: string | null;
|
||||||
depth: number;
|
depth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sidenote */
|
||||||
|
|
||||||
|
export interface Sidenote {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
marker: string;
|
||||||
|
type: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|||||||
21
src/lib/utils/extractors.ts
Normal file
21
src/lib/utils/extractors.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Markdoc from '@markdoc/markdoc';
|
||||||
|
import type { Sidenote } from '@lib/types/content';
|
||||||
|
|
||||||
|
export function extractSidenotes(raw: string): Sidenote[] {
|
||||||
|
const ast = Markdoc.parse(raw);
|
||||||
|
const sidenotes: Sidenote[] = [];
|
||||||
|
|
||||||
|
for (const node of ast.walk()) {
|
||||||
|
if (node.type === 'tag' && node.tag === 'Sidenote') {
|
||||||
|
const attrs = node.attributes;
|
||||||
|
sidenotes.push({
|
||||||
|
id: attrs.id,
|
||||||
|
marker: attrs.marker ?? '⋄',
|
||||||
|
title: attrs.title ?? '',
|
||||||
|
type: attrs.type ?? 'default',
|
||||||
|
content: attrs.content,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sidenotes;
|
||||||
|
}
|
||||||
@@ -83,7 +83,8 @@
|
|||||||
--color-overlay-heavy: oklch(0% 0 0deg / 60%);
|
--color-overlay-heavy: oklch(0% 0 0deg / 60%);
|
||||||
|
|
||||||
/* == Shadows == */
|
/* == Shadows == */
|
||||||
--color-shadow: oklch(0% 0 0deg / 10%);
|
--color-shadow: var(--space-2) var(--space-2) 0 oklch(from var(--color-surface-inverse) calc(l - 0.075) c h), var(--space-4) var(--space-4) 0 oklch(from var(--color-surface-inverse) calc(l - 0.2) c h);
|
||||||
|
;
|
||||||
--color-shadow-strong: oklch(0% 0 0deg / 25%);
|
--color-shadow-strong: oklch(0% 0 0deg / 25%);
|
||||||
|
|
||||||
/* == Utility == */
|
/* == Utility == */
|
||||||
|
|||||||
@@ -233,7 +233,7 @@
|
|||||||
contain: style;
|
contain: style;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
& li {
|
& > li {
|
||||||
@mixin ml var(--space-6);
|
@mixin ml var(--space-6);
|
||||||
@mixin pl var(--space-4);
|
@mixin pl var(--space-4);
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
|
|
||||||
border: var(--size-1) solid var(--color-border-strong);
|
border: var(--size-1) solid var(--color-border-strong);
|
||||||
color: var(--color-text-inverse);
|
color: var(--color-text-inverse);
|
||||||
text-align: left;
|
text-align: center;
|
||||||
background: var(--color-surface-inverse);
|
background: var(--color-surface-inverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,6 +311,14 @@
|
|||||||
@mixin pa var(--space-4);
|
@mixin pa var(--space-4);
|
||||||
|
|
||||||
border: var(--size-1) solid var(--color-border-subtle);
|
border: var(--size-1) solid var(--color-border-subtle);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& th,
|
||||||
|
& td {
|
||||||
|
&:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +344,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* === DETAILS / SUMMARY === */
|
/* === DETAILS / SUMMARY === */
|
||||||
& details {
|
|
||||||
|
/* & details {
|
||||||
@mixin mr var(--space-8);
|
@mixin mr var(--space-8);
|
||||||
@mixin border-l var(--size-4), solod, var(--color-border-normal);
|
@mixin border-l var(--size-4), solod, var(--color-border-normal);
|
||||||
|
|
||||||
@@ -377,7 +386,7 @@
|
|||||||
|
|
||||||
& details > *:not(summary) {
|
& details > *:not(summary) {
|
||||||
@mixin px var(--space-6);
|
@mixin px var(--space-6);
|
||||||
}
|
} */
|
||||||
|
|
||||||
& hr {
|
& hr {
|
||||||
@mixin my var(--space-10);
|
@mixin my var(--space-10);
|
||||||
@@ -411,7 +420,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* === FIGURES === */
|
/* === FIGURES === */
|
||||||
& figure {
|
|
||||||
|
/* & figure {
|
||||||
@mixin my var(--space-10);
|
@mixin my var(--space-10);
|
||||||
|
|
||||||
& img {
|
& img {
|
||||||
@@ -426,7 +436,7 @@
|
|||||||
|
|
||||||
color: var(--color-text-tertiary);
|
color: var(--color-text-tertiary);
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
/* === INLINE ELEMENTS === */
|
/* === INLINE ELEMENTS === */
|
||||||
|
|
||||||
@@ -459,6 +469,11 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.sidenote-ref {
|
||||||
|
scroll-margin-top: calc(var(--layout-header-height) + var(--size-8));
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:visited {
|
&:visited {
|
||||||
@@ -517,11 +532,11 @@
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
& cite {
|
/* & cite {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
} */
|
||||||
|
|
||||||
& q {
|
& q {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
src: url('/src/fonts/UnigrimDee-Regular.woff2') format('woff2');
|
src: url('/src/fonts/UnigrimDee-Regular.woff2') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Iosevka Slab';
|
font-family: 'Iosevka Slab';
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
@@ -147,4 +149,125 @@
|
|||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url('/src/fonts/IosevkaSansMono/IosevkaSansMono-ExtraBoldItalic.woff2')
|
src: url('/src/fonts/IosevkaSansMono/IosevkaSansMono-ExtraBoldItalic.woff2')
|
||||||
format('woff2');
|
format('woff2');
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-Light.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-Regular.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-Medium.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-SemiBold.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-Bold.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-Black.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-LightItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Julia Mono';
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,8 +146,8 @@
|
|||||||
/* === FONT FAMILIES === */
|
/* === FONT FAMILIES === */
|
||||||
--font-header: 'Geist Variable', sans;
|
--font-header: 'Geist Variable', sans;
|
||||||
--font-display: 'Blaka',serif;
|
--font-display: 'Blaka',serif;
|
||||||
--font-body: 'Iosevka Slab', sans;
|
--font-body: 'Julia Mono', sans;
|
||||||
--font-mono: 'Iosevka Mono', monospace;
|
--font-mono: 'Julia Mono', monospace;
|
||||||
--font-symbols: 'Unigrim Dee', fantasy;
|
--font-symbols: 'Unigrim Dee', fantasy;
|
||||||
|
|
||||||
/* === SIZE (REM-based) === */
|
/* === SIZE (REM-based) === */
|
||||||
@@ -205,9 +205,10 @@
|
|||||||
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
||||||
|
|
||||||
/* === LAYOUT === */
|
/* === LAYOUT === */
|
||||||
--layout-content-width: minmax(0, 72ch);
|
--layout-content-width: minmax(0, 80ch);
|
||||||
--layout-margin-width: minmax(18ch, 1fr);
|
--layout-margin-width: minmax(10ch, 1fr);
|
||||||
--layout-gutter: var(--space-6);
|
--layout-gutter: var(--space-12);
|
||||||
--layout-page-margin: var(--space-8);
|
--layout-page-margin: var(--space-8);
|
||||||
--layout-max-width: 90rem;
|
--layout-max-width: 90rem;
|
||||||
|
--layout-header-height: 3.125rem;
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/styles/mixins/patterns.css
Normal file
1
src/styles/mixins/patterns.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Reference in New Issue
Block a user