Homepage layout, motion and media refresh + /people browse ordering - #17
Open
Skaz3r wants to merge 3 commits into
Open
Homepage layout, motion and media refresh + /people browse ordering#17Skaz3r wants to merge 3 commits into
Skaz3r wants to merge 3 commits into
Conversation
Every make target that touches the database or services shells out to docker or docker-compose, so none of them work on a machine without it. Documents what the server actually requires to boot (SurrealDB only — S3 and the embedding model are both non-fatal), and gives the native equivalent of each make target: installing the surreal binary, starting it, importing the schema, and seeding. Notes two traps worth writing down: the seed files must be posted to the HTTP /sql endpoint rather than piped through `surreal sql`, whose REPL parses line by line and chokes on multi-line statements; and the rocksdb data path must avoid spaces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/people previously ordered identity-verified first and then purely by
created_at, so a profile with no photo could sit above a complete one.
Ordering is now four boolean keys: identity-verified, has a photo, has
real profile text (headline or bio rather than just a name), then
created_at DESC within each bucket. Booleans sort false < true, so DESC
puts each "yes" ahead of its "no" — an extension of the _vord idiom that
was already here rather than a new ranking system.
The query was byte-identical in two places (first page and the
infinite-scroll SSE feed) and MUST stay that way, or paging duplicates
some people and skips others. It now lives in one PEOPLE_BROWSE_QUERY
constant so that is structural instead of a comment.
Also fixes IS NOT NULL -> IS NOT NONE in the same clause. These were the
only IS NOT NULL in the routes; everywhere else uses IS NOT NONE. An
unset option<string> is NONE, and `NONE IS NOT NULL` is truthy, so the
filter matched everything and the Rust filter_map below was doing the
real work. Measured on seed data, where nobody has a website:
WHERE profile.website IS NOT NULL -> 9 rows (all of them)
WHERE profile.website IS NOT NONE -> 0 rows
The visible set of people is unchanged — Rust already excluded those
rows — but has_more is computed from the raw row count before that
filter runs, so pages could render short while the sentinel's fixed
offset=20 stride skipped the dropped rows. SQL and Rust now agree.
Search ordering is deliberately untouched: ?filter= and the specialty
chips route through search_people and stay relevance-ranked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Layout - Sections below the hero read ~15% narrower, driven by one tunable custom property (--home-section-inset) applied as horizontal padding. Padding rather than max-width, so the full-bleed background colours and artwork still reach both edges while the content narrows. Mobile is untouched: the <=768px rules already restate their own padding. - Hero copy gets a left inset that tapers to zero at 900px. The taper is load-bearing — the left column is only ~45% of the viewport, and a fixed inset squeezed the CTA row until the second button overflowed. - Hero grid switched to minmax(0, ...) and the CTA row now wraps. A bare fr track will not shrink below min-content, which let an over-wide item push the grid past the viewport and scroll the page sideways. - #why-features drops 5 -> 3 columns between 769 and 1100px, where five columns got too tight once inset. Range query on purpose: an open-ended max-width would leak into the 2-column mobile layout. - "What's Next" is centred on mobile only, with roomier gutters. Motion (new convention — see docs) - Adds the repo's first scroll reveal: [data-reveal] elements fade up as they enter view, applied to Why SlateHub, Organizations and What's Next. Fail-open by construction — the hidden state is gated behind a flag that scroll-reveal.js sets itself, so a script that fails to load leaves content visible rather than permanently hidden. Ships an explicit reduced-motion rule, because the global one only shortens durations and cannot undo opacity: 0. - CTA pills lift on hover; #hero-tag sweeps an accent fill; the header brand slogan wakes up when the lockup is hovered or focused. Media - New hero photograph, framed with object-position so the crop protects the faces. The mobile search panel moves from 35% to 60% — at 35% it sat straight across them. - Embeds the "how is this free" video beside that section's copy, youtube-nocookie and lazy so it neither drops tracking cookies on visitors who never press play nor costs anything until scrolled near. - Roadmap phase cards gain inline SVG icons. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hey Chris — homepage design pass plus one backend change to
/peopleordering. Three commits, each reviewable on its own; the backend one shares no files with the frontend work if you want to split it up.What changed
Layout. Sections below the hero read ~15% narrower, driven by a single tunable custom property (
--home-section-inset) applied as horizontal padding rather than amax-width— these sections carry full-bleed background colours and artwork that still need to reach both edges while the content narrows. Mobile is untouched, since the ≤768px rules already restate their own padding. The hero copy gets a left inset that tapers to zero at 900px.Motion. CTA pills lift on hover,
#hero-tagsweeps an accent fill, and the header brand slogan wakes up on hover/focus. More significantly, this adds the repo's first scroll reveal — see below.Media. New hero photograph, the "how is this free" video embedded beside that section's copy, and inline SVG icons on the roadmap phase cards.
/peopleordering. Was identity-verified first, then purelycreated_at, so a profile with no photo could sit above a complete one. Now four boolean keys: verified → has photo → has real profile text → newest. It extends the_vordidiom already in the file rather than inventing a ranking system.Worth your attention
The scroll reveal is a new convention. Documented in
docs/HOMEPAGE_LAYOUT_AND_MOTION.md. Two design decisions I'd like you to sanity-check:[data-reveal-ready]on<html>, whichscroll-reveal.jssets itself. If the script fails to load, the attribute is never set, the hidden rule never matches, and everything renders normally. This is why the flag and the revealer must stay in one file, and why the script is loaded synchronously in<head>rather than deferred — it has to arm the CSS before the body paints, or revealed content flashes in and back out. Both constraints are commented at the call sites.prefers-reduced-motionrule. The global block only sets durations to0.01ms; it cannot undo anopacity: 0start state, so without this reduced-motion users would see nothing at all.I chose this over Datastar's
data-on-intersect, which fires a server request rather than a visual toggle.The two
/peoplequeries were byte-identical in two places (first page and the SSE feed) and must stay that way or paging duplicates some people and skips others. They now share onePEOPLE_BROWSE_QUERYconstant, so that's structural instead of a comment.IS NOT NULL→IS NOT NONEin that same clause. These were the onlyIS NOT NULLin the routes; everywhere else usesIS NOT NONE. An unsetoption<string>isNONE, andNONE IS NOT NULLis truthy, so the filter matched everything and the Rustfilter_mapbelow was doing the real work. Measured on seed data, where nobody has a website:The visible set of people is unchanged — Rust already excluded those rows — but
has_moreis computed from the raw row count before that filter runs, so pages could render short while the sentinel's fixedoffset=20stride skipped the dropped rows.First
color-mix()in the codebase, used for the hero gradient so it derives from--color-accentinstead of a literal. The older gradients inmain.cssuse rawrgba()and predate the token rule. Flagging it as a new pattern rather than assuming it's welcome.#why-featuresdrops 5 → 3 columns between 769px and 1100px. This is the only part of the layout work that changes structure rather than spacing — five columns got genuinely tight once inset.Deliberately not done
?filter=and the specialty chips route throughsearch_peopleand stay relevance-ranked, so ordering still changes character once someone searches. Pre-existing; happy to follow up.Testing
cargo buildpasses.make testcould not be run — it needs Docker, which isn't available on this machine, so the test suite is unverified on this branch. Please run it before merging.Everything else was verified by measurement in a real browser at 375 / 769 / 820 / 1000 / 1100 / 1150 / 1440 / 1700px: no horizontal overflow from homepage content at any width, hero right column pixel-identical to before, section backgrounds still full-bleed, video locked to 16:9, and
/peoplereturning the intended order against seeded data.Two things I could not verify and would like a second pair of eyes on: the scroll reveal actually firing (the headless browser I had runs as a hidden document, and
IntersectionObserverdoesn't deliver in one — everything around it is verified, but not the trigger itself), and the hover states, for the same reason.One known limitation: the new hero image is 1024×1024, against 2499×1666 for the one it replaces, so it renders at roughly half the pixel density a high-DPI display wants in that column and looks soft. A larger original would be a drop-in improvement.
🤖 Generated with Claude Code