feat(settings): menu and body-text font size, and a bolder menu by default

The sidebar read as muted 13px; the menu items now use their own size at
medium weight so they carry at a glance (Todoist-ish). Both sizes are
choices under Settings → Appearance:

- Menu font size drives a dedicated --menu-size the sidebar uses.
- Body text size re-scales the shared type tokens (the list and the notes),
  the same mechanism a look uses.

The two are independent on purpose — one is a glance down a list of names,
the other a paragraph you read. Kept in localStorage like theme and look,
and applied in app.html before first paint so a chosen size does not flash
in and resize. Four steps each (Small/Default/Large/Largest); Default
carries no attribute so the base tokens stand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kristianwind
2026-09-02 15:30:30 +02:00
co-authored by Claude Opus 4.8
parent c0c1281415
commit 8dfab618f7
8 changed files with 217 additions and 3 deletions
+5 -2
View File
@@ -1441,7 +1441,10 @@
border-radius: var(--radius);
color: var(--ink-muted);
text-decoration: none;
font-size: var(--text-sm);
/* Its own size (Settings → Appearance) and medium weight, so the menu items
carry more than the muted small text they used to and read at a glance. */
font-size: var(--menu-size);
font-weight: 500;
transition:
background var(--fast) var(--ease),
color var(--fast) var(--ease);
@@ -1455,7 +1458,7 @@
a.active {
background: var(--surface-raised);
color: var(--ink);
font-weight: 500;
font-weight: 600;
}
/* Only your own projects reorder. A shared one sits where its owner put it —
+8
View File
@@ -230,6 +230,14 @@ export const da = {
'Gemmes i denne browser. Det er en egenskab ved den skærm, du sidder ved — en bærbar i solen og en skærm i et mørkt rum vil have hvert sit svar.',
'account.look': 'Udseende',
'account.lookHint': 'Temaet siger hvor lyst. Det her siger hvordan det læses — skrift, luft og hjørner. De to kan kombineres frit.',
'account.sizes': 'Skriftstørrelse',
'account.sizesHint': 'Menuen og brødteksten kan sættes hver for sig — den ene er et blik ned over en liste, den anden en tekst, man læser.',
'account.menuSize': 'Menu',
'account.textSize': 'Brødtekst',
'size.small': 'Lille',
'size.default': 'Standard',
'size.large': 'Stor',
'size.xl': 'Størst',
'look.verdande': 'Verdande',
'look.verdandeNote': 'Som du kender den.',
'look.rolig': 'Rolig',
+8
View File
@@ -226,6 +226,14 @@ export const en = {
'Kept in this browser. It is a property of the screen you are sitting at — a laptop in the sun and a monitor in a dark room want different answers.',
'account.look': 'Appearance',
'account.lookHint': 'The theme says how bright. This says how it reads — typeface, air and corners. The two combine freely.',
'account.sizes': 'Text size',
'account.sizesHint': 'The menu and the body text set separately — one is a glance down a list, the other a paragraph you read.',
'account.menuSize': 'Menu',
'account.textSize': 'Body text',
'size.small': 'Small',
'size.default': 'Default',
'size.large': 'Large',
'size.xl': 'Largest',
'look.verdande': 'Verdande',
'look.verdandeNote': 'As you know it.',
'look.rolig': 'Calm',
+46
View File
@@ -1051,6 +1051,52 @@ class Look {
export const look = new Look();
/**
* The font-size steps, shared by the menu and the body-text controls. `default`
* carries no attribute, so the base tokens in app.css stand unchanged.
*/
export const SIZES = [
{ id: 'small', name: 'size.small' },
{ id: 'default', name: 'size.default' },
{ id: 'large', name: 'size.large' },
{ id: 'xl', name: 'size.xl' }
];
/**
* A font-size preference — the same shape as Look: kept in localStorage and
* mirrored onto <html>, where app.html sets it before first paint so a chosen size
* does not flash in at the default and resize. Two instances below, because the
* menu and the body text are separate questions.
*/
class Sizing {
current = $state('default');
#key;
#attr;
constructor(key, attr) {
this.#key = key;
this.#attr = attr;
if (typeof document !== 'undefined') {
this.current = document.documentElement.dataset[attr] ?? 'default';
}
}
set(id) {
if (!SIZES.some((s) => s.id === id)) return;
this.current = id;
if (id === 'default') delete document.documentElement.dataset[this.#attr];
else document.documentElement.dataset[this.#attr] = id;
try {
localStorage.setItem(this.#key, id);
} catch {
// Private browsing; the choice simply will not persist.
}
}
}
export const menuSize = new Sizing('verdande:menu-size', 'menuSize');
export const textSize = new Sizing('verdande:text-size', 'textSize');
/** Theme, kept in localStorage and applied to the document element. */
class Theme {
/**