Skip to content

fix(studio): improve font picker and text property controls#736

Merged
vanceingalls merged 1 commit into
nextfrom
fix/studio-text-controls
May 12, 2026
Merged

fix(studio): improve font picker and text property controls#736
vanceingalls merged 1 commit into
nextfrom
fix/studio-text-controls

Conversation

@vanceingalls
Copy link
Copy Markdown
Collaborator

Summary

Six improvements to Studio's text property panel and font picker.

  • Line height / letter-spacing: converted from free-text MetricField to SelectField with standard presets (line: normal–2, track: -0.05em–0.2em)
  • Font style: removed oblique option (falls back to italic in most fonts), simplified to normal/italic
  • Font weight: detects available weights via document.fonts.check() for the current font family, filters dropdown to supported values, adds human-readable labels (Thin, Light, Regular, Bold, etc.)
  • Font source labeling: local fonts that match Google Fonts catalog are tagged as "Google" instead of "Local" for clarity
  • Font list balancing: per-source caps (100 Google, 80 Local, all System) prevent any source from being pushed off the dropdown by another
  • Sort order: Google fonts rank before Local so curated fonts appear first in the list

Test plan

  • Select a text element — Line and Track should be dropdowns with presets
  • Style should show normal/italic only (no oblique)
  • Weight should show labeled options, filtered to available weights for the current font
  • Font picker should show Google fonts labeled as "Google", not "Local"
  • All three source types (Google, Local, System) should be visible in the font list
  • Searching fonts should return results from all sources

🤖 Generated with Claude Code

- Line height and letter-spacing: convert from free-text to select with presets
- Font style: remove oblique (browser falls back to italic), keep normal/italic
- Font weight: detect available weights via document.fonts.check(), add labels
- Font source: local fonts matching Google catalog tagged as Google
- Font list: balanced per-source caps prevent any source from being cut off
- Sort order: Google fonts rank before Local so curated fonts appear first

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — six small fixes, all coherent

Read the full PropertyPanel.tsx (~3000 LoC) end-to-end for the relevant pieces. All six PR-description claims verified against source.

Backward-compat check — the only thing that could regress

My main concern walking in was: converting free-text MetricField → preset-only SelectField for line-height / letter-spacing, plus dropping oblique from font-style, could leave existing compositions displaying wrong values in the picker.

Confirmed clean — SelectField at PropertyPanel.tsx:2177 defensively prepends out-of-list values:

const renderedOptions = value && !options.includes(value) ? [value, ...options] : options;

Same pattern in the new FontWeightField (displayOptions). So:

  • Saved letter-spacing: 0.07em → shows 0.07em in the dropdown, user can re-select it.
  • Saved font-style: oblique → still picker-displayable, even though oblique is removed from the new options list.
  • Saved font-weight: 350 (rare, but valid) → preserved.

The free-text → preset trade-off does mean users lose the ability to re-enter a non-preset value once committed — they're snapped to the nearest preset on the next edit. Intentional per the PR description; flagging for awareness, not a blocker.

detectAvailableWeights semantics

Worth knowing: document.fonts.check("400 16px \"Roboto\"") returns true for any weight the browser will render — including via faux-bold / faux-italic synthesis. So for a system font like Arial (which only ships 400 + 700 as real glyphs), check() will report all 9 weights as available. The dropdown then offers all 9 weights even though only two are real.

In practice this is fine — synthesis produces acceptable output for most users. But the PR description's "filters dropdown to supported weights" is slightly more precise than what's delivered ("filters to renderable weights, including synthesized"). Non-blocking observation.

Also: fonts.check() returns false for unloaded fonts. If a user has Roboto selected but the stylesheet hasn't injected yet, the picker shows all 9 weights via the available.length > 0 ? available : ALL_WEIGHTS fallback, then re-renders to the filtered list once the font loads. Brief flicker possible; tolerable.

taggedLocalFonts interaction with the existing dedup

uniqueFontOptions at 754 dedupes by family name (lowercase), first-write-wins. The new options array orders Google catalog before taggedLocalFonts, so when a font is in both googleFonts AND localFonts, the Google entry wins regardless of the tag.

The tagging logic in taggedLocalFonts is therefore load-bearing only when a font is in localFonts AND in the Google catalog but not in the googleFonts array passed as prop. If googleFonts is the full curated catalog, the tagging is redundant. If googleFonts is a subset, the tagging correctly surfaces Google-catalog-matched locals as "Google". Either way, no bug — just worth knowing the tagging is sometimes redundant.

Per-source caps in filteredOptions

Verified: 100 Google / 80 Local / unlimited System + Current + Document + Imported. Sensible — prevents Google or Local from monopolizing the dropdown.

Note: the cap only applies to the unfiltered list. When the user types a query, all matches up to 200 are returned without per-source balancing. Acceptable because search is typically narrow.

font-style removed oblique value

The PR description says "falls back to italic in most fonts." Technically, CSS font-style: oblique and italic are distinct — oblique is mechanically slanted glyphs, italic is true italic glyph variants. For most variable/web fonts, the browser DOES fall back oblique → italic if no oblique variant exists. The simplification is reasonable for a property panel; advanced users can still set oblique via CSS directly, and the SelectField prepend preserves it on display.

Praise

  • Sticky-value handling via displayOptions / renderedOptions is the right primitive for both Weight and Style. Without it, the dropdown changes would have been disruptive for existing content.
  • detectAvailableWeights correctly handles quoted/unquoted family names and the multi-family font-family declaration via split(",")[0]?.trim().replace(/['"]/g, "").
  • Per-source caps are a clean fix for the "Google fonts pushed off by Local" usability bug — much better than a single hard 160-item cap.
  • Sort-rank swap (Google before Local) matches user expectation since Google fonts are the more recognizable / curated set.

mergeable_state: "unstable" — some non-required checks failing

Worth a glance at CI before merge, but doesn't block on its own.

Review by Rames Jusso (pr-review)

Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-scoped PR. Six fixes in one file, all backward-compatible.

Verified the key risk — SelectField defensively prepends out-of-list values (:2177), so existing compositions with arbitrary letter-spacing: 0.07em or font-weight: 350 still display correctly in the picker.

Two minor observations (non-blocking):

  1. detectAvailableWeights runs 9 document.fonts.check() calls on every render of FontWeightField. Fast in practice, but if we ever notice jank in the property panel, memoizing on fontFamily would be the first thing to try.

  2. Line-height/letter-spacing presets: 0px (legacy MetricField default) and 0em both appear in the tracking list — functionally identical. Tiny UX papercut, not worth blocking on.

CI green, targets next. Good to merge.

— Magi

@vanceingalls vanceingalls merged commit 7280b94 into next May 12, 2026
11 checks passed
@vanceingalls vanceingalls deleted the fix/studio-text-controls branch May 12, 2026 02:16
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.

3 participants