mirror of
https://github.com/jaronpate/folio.git
synced 2026-10-19 01:07:39 +00:00
redaction component, margin not stacking, other cleanup
This commit is contained in:
+131
-35
@@ -92,41 +92,7 @@
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Alegreya */
|
||||
@font-face {
|
||||
font-family: 'Alegreya';
|
||||
src: url('~/assets/fonts/Alegreya HT/Alegreya-Regular.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Alegreya';
|
||||
src: url('~/assets/fonts/Alegreya HT/Alegreya-Italic.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Alegreya';
|
||||
src: url('~/assets/fonts/Alegreya HT/Alegreya-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Alegreya';
|
||||
src: url('~/assets/fonts/Alegreya HT/Alegreya-BoldItalic.woff2')
|
||||
format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Redaction */
|
||||
/* Redaction — 7 grades × Regular / Italic / Bold */
|
||||
@font-face {
|
||||
font-family: 'Redaction';
|
||||
src: url('~/assets/fonts/Redaction/Redaction-Regular.woff2') format('woff2');
|
||||
@@ -151,6 +117,58 @@
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 10';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_10-Regular.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 10';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_10-Italic.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 10';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_10-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 20';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_20-Regular.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 20';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_20-Italic.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 20';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_20-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 35';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_35-Regular.woff2')
|
||||
@@ -177,6 +195,84 @@
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 50';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_50-Regular.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 50';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_50-Italic.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 50';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_50-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 70';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_70-Regular.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 70';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_70-Italic.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 70';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_70-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 100';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_100-Regular.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 100';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_100-Italic.woff2')
|
||||
format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Redaction 100';
|
||||
src: url('~/assets/fonts/Redaction/Redaction_100-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Space Mono */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
|
||||
+49
-22
@@ -34,21 +34,6 @@ html.font-plantin {
|
||||
--body-weight: 300;
|
||||
}
|
||||
|
||||
html.font-alegreya {
|
||||
--font: 'Alegreya', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction {
|
||||
--font: 'Redaction', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-35 {
|
||||
--font: 'Redaction 35', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-sohne {
|
||||
--font: 'SohneBuch', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
--body-weight: 400;
|
||||
@@ -69,8 +54,38 @@ html.font-palatino {
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-iowan {
|
||||
--font: 'Iowan Old Style', 'Palatino Linotype', Palatino, serif;
|
||||
html.font-redaction {
|
||||
--font: 'Redaction', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-10 {
|
||||
--font: 'Redaction 10', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-20 {
|
||||
--font: 'Redaction 20', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-35 {
|
||||
--font: 'Redaction 35', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-50 {
|
||||
--font: 'Redaction 50', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-70 {
|
||||
--font: 'Redaction 70', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-redaction-100 {
|
||||
--font: 'Redaction 100', Georgia, serif;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
@@ -79,11 +94,6 @@ html.font-system {
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
html.font-courier {
|
||||
--font: 'Courier New', Courier, ui-monospace, monospace;
|
||||
--body-weight: 400;
|
||||
}
|
||||
|
||||
.dark-mode,
|
||||
html.theme-navy,
|
||||
html.theme-dusk,
|
||||
@@ -351,6 +361,7 @@ article .article-body > :first-child {
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.86em;
|
||||
color: var(--text);
|
||||
background: var(--code-bg);
|
||||
padding: 0.12em 0.35em;
|
||||
border-radius: 3px;
|
||||
@@ -359,6 +370,7 @@ code {
|
||||
pre {
|
||||
margin: 0 0 1em;
|
||||
padding: 0.9rem 1rem;
|
||||
color: var(--text);
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius);
|
||||
overflow-x: auto;
|
||||
@@ -367,10 +379,25 @@ pre {
|
||||
|
||||
pre code {
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
background: none;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
pre.shiki {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
html.light-mode .shiki span {
|
||||
color: var(--shiki-light-mode, var(--shiki-default));
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html.dark-mode .shiki span {
|
||||
color: var(--shiki-dark-mode, var(--text));
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
body {
|
||||
font-size: 17px;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { watchMarginNotes } from '~/utils/stackMarginNotes';
|
||||
|
||||
onMounted(() => {
|
||||
watchMarginNotes();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="margin-note"><slot /></span>
|
||||
<span class="margin-note-root">
|
||||
<span class="margin-note-mark" aria-hidden="true"></span>
|
||||
<span class="margin-note"><slot /></span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.margin-note-root {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.margin-note-mark {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.margin-note {
|
||||
display: block;
|
||||
margin: 0.4rem 0 1rem;
|
||||
@@ -15,7 +34,7 @@
|
||||
@media (min-width: 1200px) {
|
||||
.margin-note {
|
||||
position: absolute;
|
||||
left: calc(100% + 2rem);
|
||||
left: calc(100% + var(--note-gap));
|
||||
width: var(--note-width);
|
||||
margin: 0;
|
||||
font-size: 0.8rem;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
redactionFamily,
|
||||
resolveRedactionStrength,
|
||||
} from '~/utils/fonts';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
strength?: number | string;
|
||||
}>(),
|
||||
{
|
||||
strength: 35,
|
||||
},
|
||||
);
|
||||
|
||||
const grade = computed(() => resolveRedactionStrength(props.strength));
|
||||
const family = computed(
|
||||
() => `'${redactionFamily(grade.value)}', Georgia, serif`,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
class="redacted"
|
||||
:data-strength="grade"
|
||||
:style="{ fontFamily: family }"
|
||||
><slot /></span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.redacted {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
:where(strong, b) .redacted {
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
@@ -53,7 +53,7 @@ const onFontChange = (event: Event) => {
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="theme-picker">
|
||||
<!-- <label class="theme-picker">
|
||||
<span class="visually-hidden">Font</span>
|
||||
<select
|
||||
class="theme-select"
|
||||
@@ -68,7 +68,7 @@ const onFontChange = (event: Event) => {
|
||||
{{ font.label }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
</label> -->
|
||||
</nav>
|
||||
</div>
|
||||
<h1 class="hero__title">{{ title }}</h1>
|
||||
|
||||
+30
-8
@@ -1,15 +1,18 @@
|
||||
export const BODY_FONTS = [
|
||||
{ id: 'plantin', label: 'Plantin' },
|
||||
{ id: 'alegreya', label: 'Alegreya' },
|
||||
{ id: 'redaction', label: 'Redaction' },
|
||||
{ id: 'redaction-35', label: 'Redaction 35' },
|
||||
{ id: 'sohne', label: 'Sohne' },
|
||||
{ id: 'space', label: 'Space Mono' },
|
||||
{ id: 'georgia', label: 'Georgia' },
|
||||
{ id: 'palatino', label: 'Palatino' },
|
||||
{ id: 'iowan', label: 'Iowan' },
|
||||
{ id: 'redaction', label: 'Redaction' },
|
||||
// These are to be used by components or other styles
|
||||
// { id: 'redaction-10', label: 'Redaction 10' },
|
||||
// { id: 'redaction-20', label: 'Redaction 20' },
|
||||
// { id: 'redaction-35', label: 'Redaction 35' },
|
||||
// { id: 'redaction-50', label: 'Redaction 50' },
|
||||
// { id: 'redaction-70', label: 'Redaction 70' },
|
||||
// { id: 'redaction-100', label: 'Redaction 100' },
|
||||
{ id: 'sohne', label: 'Sohne' },
|
||||
{ id: 'georgia', label: 'Georgia' },
|
||||
{ id: 'system', label: 'System Sans' },
|
||||
{ id: 'courier', label: 'Courier' },
|
||||
{ id: 'space', label: 'Space Mono' },
|
||||
] as const;
|
||||
|
||||
export type BodyFontId = (typeof BODY_FONTS)[number]['id'];
|
||||
@@ -20,3 +23,22 @@ export const BODY_FONT_STORAGE_KEY = 'body-font';
|
||||
export function isBodyFontId(value: string | null): value is BodyFontId {
|
||||
return BODY_FONTS.some((font) => font.id === value);
|
||||
}
|
||||
|
||||
export const REDACTION_STRENGTHS = [0, 10, 20, 35, 50, 70, 100] as const;
|
||||
|
||||
export type RedactionStrength = (typeof REDACTION_STRENGTHS)[number];
|
||||
|
||||
export function redactionFamily(strength: RedactionStrength): string {
|
||||
return strength === 0 ? 'Redaction' : `Redaction ${strength}`;
|
||||
}
|
||||
|
||||
export function resolveRedactionStrength(value: unknown): RedactionStrength {
|
||||
const n = Number(value);
|
||||
if (REDACTION_STRENGTHS.includes(n as RedactionStrength)) {
|
||||
return n as RedactionStrength;
|
||||
}
|
||||
if (!Number.isFinite(n)) return 35;
|
||||
return REDACTION_STRENGTHS.reduce((best, grade) =>
|
||||
Math.abs(grade - n) < Math.abs(best - n) ? grade : best,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
const WIDE = '(min-width: 1200px)';
|
||||
const GAP = 8;
|
||||
|
||||
export function stackMarginNotes() {
|
||||
if (!import.meta.client) return;
|
||||
|
||||
const notes = Array.from(
|
||||
document.querySelectorAll<HTMLElement>('.margin-note'),
|
||||
);
|
||||
|
||||
if (!window.matchMedia(WIDE).matches) {
|
||||
for (const note of notes) note.style.top = '';
|
||||
return;
|
||||
}
|
||||
|
||||
for (const note of notes) note.style.top = '';
|
||||
|
||||
let previousBottom = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (const note of notes) {
|
||||
const article = note.closest('article');
|
||||
const mark = note
|
||||
.closest('.margin-note-root')
|
||||
?.querySelector<HTMLElement>('.margin-note-mark');
|
||||
if (!article || !mark) continue;
|
||||
|
||||
const articleTop = article.getBoundingClientRect().top;
|
||||
let top = mark.getBoundingClientRect().top - articleTop;
|
||||
if (top < previousBottom + GAP) {
|
||||
top = previousBottom + GAP;
|
||||
}
|
||||
|
||||
note.style.top = `${Math.max(0, top)}px`;
|
||||
previousBottom = top + note.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
let listening = false;
|
||||
|
||||
export function watchMarginNotes() {
|
||||
if (!import.meta.client || listening) {
|
||||
stackMarginNotes();
|
||||
return;
|
||||
}
|
||||
|
||||
listening = true;
|
||||
const run = () => stackMarginNotes();
|
||||
|
||||
window.addEventListener('resize', run);
|
||||
document.fonts?.ready.then(run);
|
||||
new ResizeObserver(run).observe(document.documentElement);
|
||||
new MutationObserver(run).observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
run();
|
||||
}
|
||||
Reference in New Issue
Block a user