Skip to content

fix(projects): a clamp that clamped nothing, and a widget you still had to hunt for - #469

Closed
catomean wants to merge 2 commits into
mainfrom
fix/project-page-followups
Closed

fix(projects): a clamp that clamped nothing, and a widget you still had to hunt for#469
catomean wants to merge 2 commits into
mainfrom
fix/project-page-followups

Conversation

@catomean

@catomean catomean commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Both found by opening the deployed page and measuring it — not by reading the diff.

1. The description clamp did nothing. line-clamp-3 clamps line boxes of inline content, but the description renders inside DescriptionEditor's full-width <button>, which produces none in the wrapper. Computed display came back flow-root instead of -webkit-box, and the element measured 386px tall with a 386px scrollHeight — a clamp reporting itself as applied (webkitLineClamp: "3") while clamping nothing. Switched to a max-height, which does not care what the child element is.

2. The widget was still behind a click. Giving Feedback its own tab fixed where it lives, not whether you can see it — the panel is still 1,054 words and setup sat behind a button labelled only "Widget". That is the exact shape of the original report: someone came to put the widget on a site, did not find it, and concluded the project had none. It had one, and it was live.

Widget state is now always on screen — a status row with a dot, Widget live / paused / enabled, not seen yet, the origin, and Manage. Only the controls stay behind a click. The three states are distinct on purpose: "live" is the observed boot heartbeat, and a token that exists but has never been seen is genuinely different from one that is working. The toggle now reads "Widget setup" and carries aria-expanded.

Measured on the live page

Before After
Identity column 333px 960px
Description 202 words, 386px tall 3 lines + expand
Words on load 2,698 290
Sections 17 stacked 6 tabs
Widget state invisible until clicked always on screen

One non-finding, recorded

A 390px probe reported every tab at 20px — under the 44px touch floor. It was a false positive: the floor lives in @media (pointer: coarse) and a resized desktop browser reports a fine pointer, so ui-tap correctly did nothing there. .ui-tap is in the coarse min-height: 2.75rem list, so real phones get 44px. Noted because the probe, not the code, was wrong — npm run audit:responsive is the tool that measures this properly.

npm run verify exits 0.

…yout bug

Reported as ugly with terrible hierarchy, and as "there is no widget" on a
project that has had a live widget the whole time. Both were true from
where the operator was standing. Measured on the live page before
changing anything:

  2,698 words · 17 sections · 8 h2s
  Visitor feedback 1,054w · Activity 847w · Report 844w
  Agent context: 37 buttons in 397 words

But the biggest cause was not the word count. On a 1680px window the
header was a `justify-between` row whose links column was `shrink-0`, so
six ghost links to ELSEWHERE took 607px of 960px — 63% — and squeezed the
project's own name and description into 333px. A 202-word description then
wrapped into a twenty-line ribbon with two thirds of the screen empty
beside it. That ribbon is what reads as a wall of text.

Three changes, in the order they matter:

1. Identity outranks navigation. Destinations move to a quiet row BELOW
   the name and description, which now get the full measure.

2. The description is clamped to three lines with a toggle. A description
   should say what a project IS; these have grown into whole briefs
   (fleetcrown's names every route, integration and monetisation rail),
   and printing all of it under the title pushed every control below the
   fold. Only offered when there is actually enough text to hide.

3. Seventeen sections become six tabs. Ten of those sections answered
   variations of one question — Overview, Status quo, What happens next,
   Purpose, Product, Reach, Build contract, Resources, Plan and finish
   line, Next action, Completion contract. A jump-nav already existed and
   indexed 6 of the 17, which its own comment named as the failure mode: a
   nav that knows about some sections teaches the reader the others are
   not there. Anchors could not fix that — scrolling into a wall leaves
   the other 2,400 words underneath. A tab removes them.

Why the widget looked missing: its setup sits inside the 1,054-word
feedback section, behind an unlabelled "Widget" toggle. Someone looking
for it did not find it and concluded the feature did not exist. It has its
own tab now.

Tabs preserve existing deep links (#feedback is what ControlInbox links
to), are arrow-key navigable as a real tablist, and keep every panel
MOUNTED while hidden — these panels hold unsaved drafts and poll while
work is in flight, so unmounting would discard a half-typed brief the
moment someone looked at another tab.

Verified with a real `next build`, not just verify: this moves JSX across
a server/client boundary and `npm run verify` deliberately excludes the
build, so it could not have caught an RSC serialization fault.
(Note for anyone repeating that: use `npx next build`. `npm run build`
has a postbuild hook that deploys.)
…ad to hunt for

Both found by opening the deployed page and measuring it, not by reading
the diff.

1. The description clamp did nothing. `line-clamp-3` clamps LINE BOXES of
   inline content, but the description renders inside DescriptionEditor's
   full-width <button>, which produces none in the wrapper. Computed
   display came back `flow-root` instead of `-webkit-box`, and the element
   measured 386px tall with a 386px scrollHeight — a clamp that reported
   itself as applied (`webkitLineClamp: "3"`) while clamping nothing.
   A max-height does not care what the child element is.

2. The widget was still behind a click. Moving Feedback into its own tab
   fixed where it lives, not whether you can see it: the panel is still
   1,054 words and widget setup was still behind a button labelled only
   "Widget". That is the exact shape of the original report — someone
   came to put the widget on a site, did not find it, and concluded the
   project had no widget. It had one, and it was live.

   Widget STATE is now always on screen — a one-row status line with a
   dot, "Widget live / paused / enabled, not seen yet", the origin, and a
   Manage button. Only the CONTROLS stay behind a click. The three states
   are distinct on purpose: "live" is the observed boot heartbeat, and a
   token that exists but has never been seen is genuinely different from
   one that is working.

   The toggle now says "Widget setup" / "Hide widget setup" rather than
   "Widget", and carries aria-expanded.
@catomean

catomean commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by a branch rebuilt on current main — this one was cut before #468's squash merge, so it conflicted and ran zero CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant