Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/src/components/PageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ export function PageView({ docId }: { docId: string }) {
ref={titleInputRef}
type="text"
aria-label="Page title"
className="w-full shrink-0 border-none bg-transparent pl-8 pr-1 text-4xl font-bold leading-tight tracking-tight text-ink-1 outline-none placeholder:text-ink-3"
className="w-full shrink-0 border-none bg-transparent px-8 text-[2.5rem] font-bold leading-tight tracking-tight text-ink-1 outline-none placeholder:text-ink-3"
value={page!.title || ''}
onChange={(e) => update({ title: e.target.value })}
onKeyDown={handleTitleKeyDown}
Expand Down
91 changes: 83 additions & 8 deletions apps/web/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,102 @@
}

/* Prose role: the page document reads at 16px (UI chrome stays 13px).

One rhythm system drives all document typography (exploration 0198):
a single flow unit between blocks, "space precedes" headings, one
list-indent step, and to-dos whose checkbox + gap equals that step so
their text shares the bullet text edge. Horizontal padding is
symmetric (the left half doubles as the drag-handle gutter), so the
measure is centred and the title and every body block share one left
edge. These `.page-prose .ProseMirror` selectors outspecify the editor
package's base `.ProseMirror` rules, so the page is the single source
of document typography; canvas/compact surfaces keep the base.

The bottom padding keeps breathing room after the last line *inside*
the editable surface, so clicking it places the caret (the editor
stretches to the full page height via flex). */
.page-prose .ProseMirror {
@apply leading-relaxed pb-[20vh];
/* Rhythm tokens — em-based, so spacing scales with the font size. */
--prose-flow: 0.75em; /* between sibling blocks */
--prose-flow-tight: 0.2em; /* between list items */
--prose-h-before: 1.7em; /* heading top — space precedes */
--prose-h-after: 0.4em; /* heading bottom */
--prose-indent: 1.55em; /* one list-indent step (~25px) */
--prose-check: 1.15em; /* checkbox size */
--page-pad-x: 2rem; /* symmetric gutter; the drag handle lives in it */

font-size: var(--font-prose-size);
line-height: 1.65;
padding-bottom: 20vh;
}

.page-prose .ProseMirror p {
@apply mb-4;
/* Symmetric padding centres the measure and replaces the old asymmetric
pl-8/pr-1; the left half doubles as the drag-handle gutter. The
`.tiptap.ProseMirror` compound outspecifies the editor's shared
`[&_.ProseMirror]:px-1/pl-8` utility, so this wins only on the page
surface (canvas/compact keep their tighter padding). */
.page-prose .ProseMirror.tiptap {
padding-left: var(--page-pad-x);
padding-right: var(--page-pad-x);
}

.page-prose .ProseMirror ul,
.page-prose .ProseMirror ol {
@apply pl-6 mb-4;
/* Flow: clear default block margins, then one unit between siblings. */
.page-prose .ProseMirror > * {
margin-top: 0;
margin-bottom: 0;
}
.page-prose .ProseMirror > * + * {
margin-top: var(--prose-flow);
}
/* Headings bind to the text they introduce: more space above than below. */
.page-prose .ProseMirror > * + :is(h1, h2, h3, h4, h5, h6) {
margin-top: var(--prose-h-before);
}
.page-prose .ProseMirror :is(h1, h2, h3, h4, h5, h6) + * {
margin-top: var(--prose-h-after);
}

.page-prose .ProseMirror li {
@apply my-1;
/* Lists: one indent step per level; markers hang in the indent gutter. */
.page-prose .ProseMirror :is(ul, ol):not([data-type='taskList']) {
padding-inline-start: var(--prose-indent);
}
.page-prose .ProseMirror li + li {
margin-top: var(--prose-flow-tight);
}
.page-prose .ProseMirror li > :is(ul, ol) {
margin-top: var(--prose-flow-tight);
}

/* Blockquote: align its text to the list-content edge for one rhythm. */
.page-prose .ProseMirror blockquote {
padding-inline-start: calc(var(--prose-indent) - 4px);
}

/* To-dos: reset the inherited list indent (the +24px bug), lay the row
out as flex, and size checkbox + gap to one indent step so the to-do
text lands on the same left edge as bullet text. The checkbox is
centred on the first line with the line-relative `lh` unit, not a
magic pixel offset, so it stays centred at any font size. */
.page-prose .ProseMirror ul[data-type='taskList'] {
padding-inline-start: 0;
list-style: none;
}
.page-prose .ProseMirror ul[data-type='taskList'] li {
gap: calc(var(--prose-indent) - var(--prose-check));
}
.page-prose .ProseMirror ul[data-type='taskList'] li > label {
margin-top: 0.35em; /* fallback for browsers without the lh unit */
/* Centre the box on the first line. The small nudge accounts for the
checkbox border + the text's optical centre sitting below the line
box centre, measured to land the box centre on the text centre. */
margin-top: calc((1lh - var(--prose-check)) / 2 + 0.2em);
}
.page-prose .ProseMirror ul[data-type='taskList'] li > label input[type='checkbox'] {
width: var(--prose-check);
height: var(--prose-check);
}

/* Inline code + code fences keep the monochrome muted background. */
.page-prose .ProseMirror code {
@apply px-1.5 py-0.5 rounded font-mono text-sm bg-muted;
}
Expand Down
Loading
Loading