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
41 changes: 23 additions & 18 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ scale relative to surrounding text without changing the prose size.
| Item or subsection title: project, paper, institution, article subsection | `--type-subheading` | 18px, Inter 500 | 1.4 |
| Prose: body, summaries, record values, course names, table cells, captions | `--type-reading` | 16px, Source Serif 4 400 | 1.65 |
| Interface: navigation, contents, Read More, degree names, diagram node titles | `--type-interface`, `--type-action` | 15px, Inter 400 (500 for actions and node titles) | 1.5 |
| Label: field labels, entry record lines, diagram stage and detail text, footer | `--type-label` | 13px, Inter 400 | 1.45 |
| Label: field labels, diagram stage and detail text, footer | `--type-label` | 13px, Inter 400 | 1.45 |
| Section margin index, directory number, contribution number | `--type-index` | 11px, Source Serif 4 400 | 1.5 |
| Inline mathematical expression | Relative to surrounding text | 1em | Intrinsic mathematical geometry |
| Displayed mathematical expression | `--type-math` | 1.15em | Intrinsic mathematical geometry |
Expand All @@ -99,10 +99,12 @@ manuscript render at weight 600, so each contribution opens with its claim; ital
run-ins and bold theorem labels remain inside mathematics papers. The hero name is the
one display exception.

Whitespace separates chapters and list items. Rules mark page-level sections, the
entries within a section, the two directories, the record strip, and diagram stages.
Do not add metric rows, pills, badges, or other pulled-out figures: a result belongs in
its sentence.
Subtraction is the rule. Each component uses at most two text styles, and small grey
labels appear only where a field needs naming (the record strip, contact details,
diagrams). Whitespace separates entries, directory rows, chapters and list items;
rules mark only page-level sections, the record strip, and diagram stages. Do not add
metric rows, pills, badges, or other pulled-out figures: a result belongs in its
sentence.

Text roles use rem units and scale with the user's text setting. Titles and headings
step down below 768px, including in the architecture viewer; the other roles remain
Expand All @@ -119,7 +121,7 @@ Experience shares About's Inter headings, Source Serif 4 prose, role sizes, grid
paper/ink palette. It is one page with five numbered sections and an anchor
directory headed Experience: Areas of Focus that stays in one column at every screen width.
Experience and Coursework use the same `SectionDirectory` component: full-width
rows separated by thin rules, with small serif numbers at the left and labels in the
rows separated by whitespace, with small serif numbers at the left and labels in the
heading size. A right-aligned View action appears on hover
and keyboard focus and remains visible on touch devices; the complete row turns
blue on hover or focus. Directory numbers run from 01 through 05 for Experience
Expand All @@ -129,19 +131,20 @@ link's accessible name. Section margin indices use the shared serif index role o
About, Experience, and Coursework. Category introductions are omitted.

On About, Experience, Coursework and the ten detail pages, content hangs on two axes
of the ten-column field (`--hang-columns`, 3fr / 7fr). From 1200px, item titles sit on
axis A and their prose on axis B, three columns in, separated by entry rules; below
1200px the title stacks above its prose. Each Experience entry shows its title in the
subheading role and, under it, a record line in the label role: the organization for
AI projects, the paper title for legal research, the position otherwise, then the
dates. These lines reuse the approved metadata verbatim. Summaries use upright black
body text. No content is truncated to fit a card. Sections end on the reading-end
guide (11 of the 12 content columns).
of the ten-column field (`--hang-columns`, 4fr / 6fr). From 1200px, item titles sit
alone on axis A and their prose on axis B, four columns in, with `--prose-inset` of
space before the reading-end guide; 64px of whitespace separates entries. Below 1200px
the title stacks above its prose. Summaries use upright black body text. No content is
truncated to fit a card. Sections end on the reading-end guide (11 of the 12 content
columns).

All body copy uses the shared ink colors. Blue marks navigation, hover, keyboard
focus, and the decision or feedback path in architecture figures.

AI entries lead with the project name. The confidentiality note closes the AI section.
AI entries lead with the project name. The confidentiality note introduces "the
following four projects", so it opens the AI section as its standfirst: directly under
the heading, on the same edge, in the reading face and supporting-ink colour. Its
heading is visually hidden and labels the aside for assistive technology.

Each of the eight AI and legal articles opens with a `LogicDiagram` in its header,
under the record strip, as the article's visual abstract. Its data lives in
Expand Down Expand Up @@ -253,7 +256,7 @@ at the shared reading size and leading, in two columns on axis B from 1200px. Al
wording and order as static text, available without JavaScript. The rail's
Back to Education link returns to the Education section. Duke University appears
once above the two degree records;
degree names use the interface size at weight 500, on axis B beside the institution. Compact notes and a thin rule
degree names use the interface size at weight 500, on axis B beside the institution. Compact notes and whitespace
separate the degree records. Profile details and contact values also use the reading
scale; field labels use the label scale. Email, GitHub, and X occupy three columns
above 540px and stack in one column at 540px and below. Long contact values can wrap
Expand All @@ -274,6 +277,8 @@ the shared title, heading, subheading and reading hierarchy; scoped spacing over
Page titles own the H1 position. About's two-line "Theodore / Ouyang" supplies its
main heading. The "Thinking mathematically / Working in AI" introduction
is an H2 below the hero; it remains the H1 on the `/education/` compatibility page.
Its two lines share the title size, the second in the muted tone, and the biography
sits below on the same left edge within the reading measure.
Experience domains use `01`–`05` indices; individual entries have no leading markers.
Top-level contributions are plain paragraphs without bullets or rules. Narrative lines stay within
the shared reading measure and reflow with the viewport.
Expand Down Expand Up @@ -370,8 +375,8 @@ hover and keyboard-focus feedback. Titles and complete summaries remain outside
the link. Summaries retain selected bold emphasis. Numerical guarantees retain their
model, horizon, payoff, and comparison scope.
It does not repeat the abstracts. Titles use the same
subheading role on axis A; supporting paragraphs use the reading role on axis B. Thin
horizontal rules separate entries. Typography, spacing, and wrapping adapt within
subheading role on axis A; supporting paragraphs use the reading role on axis B.
Whitespace separates entries. Typography, spacing, and wrapping adapt within
the shared reading frame without introducing cards or changing the palette.

Each research page provides a contents list and the manuscript's abstract, core
Expand Down
5 changes: 2 additions & 3 deletions website/app/components/SectionDirectory.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
align-items: baseline;
gap: 16px;
width: 100%;
min-height: 68px;
padding-block: 20px;
border-top: 1px solid var(--rule);
min-height: 52px;
padding-block: 10px;
color: var(--ink);
font-size: var(--type-heading);
font-weight: 400;
Expand Down
55 changes: 16 additions & 39 deletions website/app/components/experience/Experience.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,18 @@
.sectionIndex { grid-column: 1; padding-top: 0.5rem; }
.sectionBody { grid-column: 2 / span 10; min-width: 0; }
.sectionTitle {
margin: 0 0 40px;
margin: 0 0 56px;
font-size: var(--type-heading);
font-weight: 400;
line-height: var(--heading-leading);
letter-spacing: var(--heading-tracking);
text-wrap: balance;
}

/* Entries hang their title and record on axis A and their summary on axis B. */
.entry {
padding: 28px 0 32px;
border-top: 1px solid var(--rule-medium);
scroll-margin-top: 40px;
}
.entryHead { min-width: 0; }
/* Entries hang their title on axis A and their summary on axis B; whitespace,
not rules, separates them. */
.entry { scroll-margin-top: 40px; }
.entry + .entry { margin-top: 64px; }
.entryTitle {
margin: 0;
color: var(--ink);
Expand All @@ -57,16 +54,6 @@
letter-spacing: var(--subheading-tracking);
text-wrap: pretty;
}
.entryMeta {
display: grid;
gap: 2px;
margin: 10px 0 0;
color: var(--muted);
font-size: var(--type-label);
line-height: var(--label-leading);
text-wrap: pretty;
}
.entryMeta span { display: block; }
.entryBody { min-width: 0; max-width: var(--reading-measure); }
.summary { margin: 0; font-size: var(--type-reading); line-height: var(--reading-leading); text-wrap: pretty; }
.summary,
Expand All @@ -77,18 +64,8 @@
font-family: var(--font-prose);
}

.indexDisclosure {
padding: 28px 0 0;
border-top: 1px solid var(--rule-medium);
}
.indexDisclosure h3 {
margin: 0 0 8px;
color: var(--muted);
font-size: var(--type-label);
font-weight: 400;
line-height: var(--label-leading);
letter-spacing: 0;
}
/* The AI section's note is its standfirst: under the heading, on the same edge. */
.indexDisclosure { max-width: var(--reading-measure); margin: -24px 0 72px; }
.indexDisclosure p { color: var(--supporting-ink); }
.disclosure h2 {
margin: 0 0 8px;
Expand Down Expand Up @@ -228,17 +205,16 @@

@media (min-width: 1200px) {
.entry,
.indexDisclosure,
.detailPage > .contributions {
display: grid;
grid-template-columns: var(--hang-columns);
align-items: start;
}
.entryHead,
.indexDisclosure h3,
.entryTitle,
.detailPage > .contributions h2 { padding-right: var(--hang-gap); }
.entryBody { max-width: none; }
.entryBody { max-width: none; padding-right: var(--prose-inset); }
.detailPage > .contributions h2 { margin: 0; }
.detailPage > .contributions > ol { padding-right: var(--prose-inset); }
}

@media (768px <= width < 1024px) {
Expand All @@ -247,10 +223,9 @@
}

@media (width < 1200px) {
.entryMeta { margin-bottom: 14px; }
.entryTitle { margin-bottom: 12px; }
.metadata { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.detailPage > .contributions > ol,
.indexDisclosure p { max-width: var(--reading-measure); }
.detailPage > .contributions > ol { max-width: var(--reading-measure); }
}

@media (width < 768px) {
Expand All @@ -260,8 +235,10 @@
.detailPage .detailHeader h1 { max-width: none; }
.section { display: block; padding: 40px 0 56px; scroll-margin-top: 6rem; }
.sectionIndex { display: block; margin-bottom: 10px; padding-top: 0; }
.sectionTitle { margin-bottom: 28px; }
.entry { padding: 24px 0 28px; scroll-margin-top: 6rem; }
.sectionTitle { margin-bottom: 32px; }
.indexDisclosure { margin: -8px 0 48px; }
.entry { scroll-margin-top: 6rem; }
.entry + .entry { margin-top: 48px; }
.metadata { gap: 16px 24px; margin-top: 32px; }
.contributions > ol > li { grid-template-columns: 1.75rem minmax(0, 1fr); }
}
Expand Down
19 changes: 4 additions & 15 deletions website/app/components/experience/ExperienceContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,10 @@ import styles from "./Experience.module.css";
function ReadingArrow() {
return <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="m9 5 7 7-7 7" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /></svg>;
}
/** The record line under an entry title: who it was with (or the paper it produced), then when. */
function entryRecord(entry: ShowcaseEntry) {
const context = entry.categoryId === "01" ? entry.organization
: entry.categoryId === "03" ? entry.detailTitle
: entry.metadata.position;
return [context, entry.metadata.dates];
}
function ExperienceEntry({ entry }: { entry: ShowcaseEntry }) {
return (
<article className={styles.entry} id={entry.slug} data-experience-entry={entry.id}>
<div className={styles.entryHead}>
<h3 className={styles.entryTitle}>{entry.listingTitle}</h3>
<p className={styles.entryMeta} data-experience-record>
{entryRecord(entry).map((line) => <span key={line}>{line}</span>)}
</p>
</div>
<h3 className={styles.entryTitle}>{entry.listingTitle}</h3>
<div className={styles.entryBody}>
<p className={styles.summary} data-experience-summary>{entry.categoryId === "01" ? <InlineMarkdown text={entry.summary} /> : entry.summary}</p>
<Link className={styles.readMore} href={entry.detailPath} aria-label={`Read more about ${entry.pageTitle}`}>
Expand All @@ -39,11 +27,12 @@ export function ExperienceSection({ domain, standalone = false }: { domain: Show
<span className={styles.sectionIndex} aria-hidden="true">{domain.number}</span>
<div className={styles.sectionBody}>
<Heading className={styles.sectionTitle} id={`${domain.slug}-heading`} tabIndex={-1}>{domain.name}</Heading>
{domain.entries.map((entry) => <ExperienceEntry entry={entry} key={entry.id} />)}
{/* The note introduces "the following four projects", so it opens the section. */}
{domain.disclosure && <aside className={styles.indexDisclosure} aria-labelledby={`${domain.slug}-disclosure`} data-experience-disclosure>
<h3 id={`${domain.slug}-disclosure`}>{domain.disclosure.title}</h3>
<h3 className="visually-hidden" id={`${domain.slug}-disclosure`}>{domain.disclosure.title}</h3>
<p><InlineMarkdown text={domain.disclosure.text} /></p>
</aside>}
{domain.entries.map((entry) => <ExperienceEntry entry={entry} key={entry.id} />)}
</div>
</section>
);
Expand Down
7 changes: 5 additions & 2 deletions website/app/components/experience/LogicDiagram.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

container: diagram / inline-size;
margin: 72px 0 0;
padding-top: 18px;
border-top: 1px solid var(--rule-strong);
}

.label {
Expand Down Expand Up @@ -217,6 +215,11 @@
border-right: 1px solid var(--accent);
}

/* The return rule under the stages between the loop's two ends. */
.loopSpan {
min-width: 0;
}

.loopLabel {
margin: 0;
padding: 10px 12px 0;
Expand Down
23 changes: 19 additions & 4 deletions website/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@
--title-tracking: -0.022em;
--heading-tracking: -0.015em;
--subheading-tracking: -0.006em;
/* Hanging layout: titles on axis A (3 of 10 columns), prose on axis B (7 of 10).
The gap belongs to the title cell so axis B lands exactly on a grid line. */
--hang-columns: minmax(0, 3fr) minmax(0, 7fr);
/* Hanging layout: titles on axis A (4 of 10 columns), prose on axis B (6 of 10).
The gap belongs to the title cell so axis B lands exactly on a grid line,
and prose keeps an inset from the reading-end guide. */
--hang-columns: minmax(0, 4fr) minmax(0, 6fr);
--hang-offset: 40%;
--hang-gap: 40px;
--prose-inset: 32px;
--link-underline-offset: 0.18em;
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
Expand Down Expand Up @@ -111,6 +114,18 @@ button:focus-visible {
background: var(--accent);
}

.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}

.skip-link {
position: fixed;
z-index: 200;
Expand Down Expand Up @@ -189,7 +204,7 @@ button:focus-visible {
}

.structural-grid[data-guides="index"] .structural-guide-reading-measure {
grid-column: 8;
grid-column: 9;
}

@media (width < 1200px) {
Expand Down
Loading
Loading