Make the writing surface a sheet of paper

It was a page-shaped box that was not page-coloured: the same fill as everything
around it with a hairline border, so it read as an empty panel. Now it is warm
off-white in daylight and a raised surface at night — deliberately not a white
rectangle, since a page that glows brighter than the room is what makes an app
unusable in the evening.

The line ran to 84 characters, past the width at which the eye reliably finds the
start of the next line, and that was the single biggest reason the surface read
badly. Measured, not guessed: 620px of sheet with a 56px margin gives about 73.
It is centred now — it used to sit 28px from the left and stop short of the
right — with almost square corners instead of a dialog's 14px radius.

Blocks on the sheet are tinted from paper rather than from the app background,
or they read as sitting underneath it.

Five German strings survived the earlier pass, "Alle Notizen exportieren" among
them: that sweep filtered on umlauts, so sentences without one went straight
through. Swept again by word.

218 unit tests, 244 browser checks across eleven suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-11 12:08:54 +02:00
1 parent 11f37b7921
commit c12ffa250e
10 files changed
+82 -27

No files matched your search

+31 -11
View File
@@ -75,6 +75,15 @@ export function NoteEditor({
const T = useMemo(
() => ({
bg: dark ? "#0d1117" : "#ffffff",
/* The sheet.
*
* In daylight it is warm off-white — plain white next to a white toolbar
* is not a sheet, it is an absence of one. At night it is a raised surface
* rather than a white rectangle: a page that glows brighter than the room
* is what makes an app unusable in the evening, and "paper" in a dark room
* still means "the surface I write on", not "white". */
paper: dark ? "#1b2430" : "#fbfaf7",
paperEdge: dark ? "#333f4e" : "#e4dfd5",
toolbar: dark ? "#161b22" : "#f6f8fa",
border: dark ? "#30363d" : "#d0d7de",
text: dark ? "#e6edf3" : "#1f2328",
@@ -154,7 +163,13 @@ export function NoteEditor({
const mobile = window.innerWidth < 768;
setIsMobileViewport(mobile);
const sidePad = mobile ? 10 : 28;
const desktopMax = 760;
/* How wide a sheet gets.
*
* With 56px of inner margin this leaves a text column of about 68
* characters at the body size — measured, not guessed. The old 760 ran to
* nearly 90, past the width at which the eye reliably finds the start of
* the next line, and the single biggest reason the surface read badly. */
const desktopMax = 620;
const minW = mobile ? 220 : 320;
const maxAllowed = Math.max(220, W - sidePad * 2);
const targetW = mobile ? maxAllowed : Math.min(desktopMax, maxAllowed);
@@ -445,11 +460,9 @@ export function NoteEditor({
* width a property of the window, which is what it always looked like. */
flex: 1,
minWidth: 0,
background: isMobileViewport
? T.bg
: dark
? "linear-gradient(180deg,#0f141b 0%,#0d1117 60%)"
: "linear-gradient(180deg,#f8fafd 0%,#eef3f8 70%)",
// The desk the sheet lies on, a touch darker than the sheet so the
// shadow has something to fall onto.
background: isMobileViewport ? T.paper : dark ? "#0b0f14" : "#eceae5",
}}
>
{/* Main toolbar */}
@@ -660,11 +673,18 @@ export function NoteEditor({
position: "relative",
width: stageSize.w,
minHeight: stageSize.h,
margin: isMobileViewport ? "0 auto" : "0 0 0 28px",
background: T.bg,
border: isMobileViewport ? "none" : `1px solid ${T.border}`,
borderRadius: isMobileViewport ? 0 : 14,
boxShadow: isMobileViewport ? "none" : "0 16px 36px rgba(0,0,0,0.14)",
// Centred. It used to sit 28px from the left and stop short of the
// right, so the margins were visibly unequal.
margin: "0 auto",
background: T.paper,
border: isMobileViewport ? "none" : `1px solid ${T.paperEdge}`,
borderRadius: isMobileViewport ? 0 : 3,
// Paper lies on something; a sheet with no shadow is a panel.
boxShadow: isMobileViewport
? "none"
: dark
? "0 10px 30px rgba(0,0,0,0.45)"
: "0 10px 28px rgba(60,50,30,0.13)",
}}
>
<div
+1 -1
View File
@@ -93,7 +93,7 @@ function SeparatorRow({ T, separator, editing, onStartRename, onRename, onContex
}
}}
aria-label="Separator label"
placeholder="Abschnitt"
placeholder="Section"
style={{
width: "100%",
minWidth: 0,
+1 -1
View File
@@ -109,7 +109,7 @@ export function PdfPins({
)}
<button
data-pin={pin.id}
title={pin.text || "Notiz zu dieser Stelle"}
title={pin.text || "Note on this passage"}
onPointerDown={(e) => startDrag(e, pin)}
onPointerMove={onPointerMove}
onPointerUp={(e) => endDrag(e, pin)}