Skip to content

feat(studio): inspector fields, toggles and sections on the shared primitives - #3774

Draft
miguel-heygen wants to merge 1 commit into
feat/studio-u13-renders-sweepfrom
feat/studio-u10a-inspector-primitives-layout-text
Draft

feat(studio): inspector fields, toggles and sections on the shared primitives#3774
miguel-heygen wants to merge 1 commit into
feat/studio-u13-renders-sweepfrom
feat/studio-u10a-inspector-primitives-layout-text

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Lands the first inspector sweep PR (U10a: primitives, layout, text, sections, the 3D Transform heading) of the Studio design-system foundation. Stacked on the Renders sweep (#3772). Ratchet 470 to 468. Bundle +7.0 KB gzipped. Stops at a seam: the flat slider and select row keep their hand-rolled implementations and move in the next PR.

What

Lands U10a, the first of the inspector sweep PRs: the inspector's shared value
controls move onto the design system's primitives, and the doubled "3D Transform"
heading is gone.

  • CommitField is a boxed field. It wears the same fieldBase as Input,
    NumberField and Select, so the inspector's metric input and every other
    Studio field are one control with one look.
  • MetricField and DetailField put their label outside that box.
  • SliderControl becomes the shared Slider, SegmentedControl the shared
    Tabs, SelectField the shared Select, FlatToggle the shared Toggle.
  • FlatRow drops the underline it drew around the value; the field's own box is
    the boundary now, and the value tier tints it.
  • The Layout section stops printing a "3D Transform" label above a component that
    already renders one.
  • Section and the 3D Transform header expand on the duration-expand token.
  • Colour literals in the ratchet: 470 to 468, with two files reaching zero.

Also fixes a defect in the shared Toggle that this migration surfaced: Base UI
renders the switch as a <span role="switch">, so disabled:opacity-40 compiled
to a real rule that could never match and a disabled switch drew at full opacity
with a pointer cursor. It is data-[disabled]: now, with a test beside the
primitive that fails without the fix.

Why

Two requirements from the design-system contract.

R10 asks that inspector inputs read as inputs, with a boundary that differs from
their label. They did not: a value was bare text on the panel background, and the
metric input measured 17px high with no radius and no background. You could not
tell a value from its own label until you clicked one.

R9 asks that "3D Transform" render once for a selected element. It rendered twice:
the Layout section printed a static label directly above a component whose own
header carries the same words and is the one that collapses.

R8 and KTD7 want one implementation per control. The hand-rolled range input,
segmented strip and native <select> were three more. The segmented strip in
particular carried aria-pressed buttons with no keyboard handling, so the only
way through it was Tab, one segment at a time; on Tabs it answers arrow keys,
Home and End.

How

The public prop signatures of the migrated primitives do not change, so their
twenty-odd consumers across the inspector pick up the new look and the new
keyboard behaviour without a line of their own changing. That is what keeps this
PR small while the visible change is panel-wide.

Telemetry keeps its contract: one event per commit boundary, never per
intermediate value. Each migrated control passes the tracker the section already
holds as onTrack, and the assertions run through a real section rather than the
primitive alone, because the section is what holds the tracker.

data-flat-row-value is deleted rather than moved. The boxed field already
carries data-testid="inspector-field", so a second hook on the same element was
one more thing to keep in step.

Test plan

Studio suite, from packages/studio, with a capped worker pool:

  • bunx vitest run --poolOptions.forks.maxForks=4 — 440 files, 4845 tests, green.
    Baseline on the branch point was 440 files / 4841 tests, so the delta is the
    four tests added here and no test removed or weakened.
  • bun run typecheck — clean.
  • bun run --filter @hyperframes/studio build — succeeds.
  • bunx oxlint and bunx oxfmt --check on the changed files — clean.
  • bunx fallow audit --fail-on-issues — exit 0 against both the base branch and
    the trunk; no finding in a file this PR touches.
  • Studio load smoke, the CI job's own script against a local dev server —
    PASS: studio loaded with schema-valid API fixtures and no runtime errors.

New tests, each proven to fail against the unfixed code:

  • The Layout section renders "3D Transform" exactly once. Fails with the removed
    label put back.
  • Section expands on duration-expand. Fails with a hard-coded duration.
  • The migrated Select trigger classifies the way the native <select> did on
    both hotkey selector lists, asserted true-and-equal so two falses cannot agree
    and prove nothing.
  • The segmented control renders a real tablist and answers ArrowRight.
  • The shared Toggle wears its disabled look on the attribute Base UI sets.

Screenshots and computed styles, before and after, from the sweep's capture
script. The row this unit is judged on:

Control Height Radius Font size Background
Inspector metric input, before 17px 0px 11px transparent
Inspector metric input, after 28px 6px 11px rgb(22, 22, 24)

28px and 6px are what both Export buttons already measure, so the inspector field
now sits inside the same three-height, three-radius set the sweep is aiming at.
Every other row in the table is identical before and after.

Bundle, gzipped across the app's chunks: 1,571,013 bytes before, 1,578,200 after.
A delta of 7,187 bytes (about 7.0 KB) for the first inspector consumers of
Slider and Toggle, against the 100 KB budget for the set. Reported, not
trimmed.

Not covered

  • FlatSlider and FlatSelectRow keep their hand-rolled implementations.
    Both are large pointer-capture and native-select state machines with about
    thirty tests each pinned to their exact DOM, and converting them would have
    taken this PR well past a reviewable size. They are the next sweep PR's, and
    the flat inspector therefore still shows one underlined row (Weight) beside the
    boxed ones until then.
  • The remaining section families are untouched: effects, colour and colour
    grading, canvas menu, media, motion and overlays keep their own colours and
    controls. Those are the later inspector PRs.
  • Four colour literals in files this PR touches stay. One is an
    rgb(0, 0, 0) CSS fallback for a computed style, which is a browser default
    rather than a design value. The other three are shadow presets that have no
    matching token yet; adding tokens belongs to the unit that owns the token file.
  • A pre-existing flake in the screenshot capture, not fixed here. The canvas
    motion-path toggle appears in some captures and not others, on the base branch
    as well as this one (present in one of two base runs, absent in two runs here).
    Its visibility is gated on an async runtime probe of the preview iframe, so it
    races the screenshot. Anyone diffing the inspector screenshots should expect it
    and not read it as a regression.
  • This PR is stacked. Its own diff is 583 changed lines; a line-count check
    measured against the trunk will report the whole stack instead.

The inspector's value control is a box now, not bare text with an
underline drawn around it. CommitField wears the same fieldBase every
other Studio field wears, so the metric input measures 28px high with a
6px radius and a real border where it measured 17px, no radius and no
background. The label moves outside that box, which is the whole of R10:
a boundary that differs from the label.

SliderControl, SegmentedControl and SelectField become the shared Slider,
Tabs and Select. The segmented strip gains arrow keys, Home and End from
Tabs; the aria-pressed buttons it replaces answered no key but Tab. The
native select is gone, so no OS popup is left in the classic panel. Each
one still fires one telemetry event per commit boundary, now proven
through a real section rather than the primitive alone.

FlatToggle becomes the shared Toggle, and that migration found a defect in
the primitive: Base UI renders the switch as a span, so Toggle's
disabled:opacity-40 compiled to a rule that could never match and a
disabled switch drew at full opacity with a pointer cursor. It is
data-[disabled] now, with a test at the primitive that fails without it.

The Layout section stops printing its own "3D Transform" label above a
component that already renders a collapsible header with the same words,
so the heading appears once (R9). Section and the 3D header expand on the
duration-expand token, which zeroes itself under prefers-reduced-motion.

Colour literals: 470 to 468.
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