Skip to content

Reuse Steam's native Friends styles for theme compatibility - #1

Merged
Alex979 merged 32 commits into
mainfrom
native-steam-classes
Aug 19, 2026
Merged

Alex979 merged 32 commits into
mainfrom
native-steam-classes

Conversation

@Alex979

@Alex979 Alex979 commented Aug 17, 2026 •

Copy link
Copy Markdown
Owner

Why

A PluginDatabase reviewer pointed out in SteamClientHomebrew/PluginDatabase#221 (comment) that the plugin re-implemented Steam's Friends styling by hand, with hardcoded colors and fonts. That meant Millennium themes couldn't touch it. Every theme would need special-case support for the Chats tab, and any Steam restyle would leave the plugin looking out of date.

What changed

The injected UI now uses Steam's own class names instead of imitating them:

  • Tab: copies its classes off the live native tab, so it renders (and themes) exactly like the FRIENDS tab. The active state is handled by toggling Steam's real activeTab class between the two tabs, so theme and high-contrast active styling always follows the actually-selected tab.
  • Search, rows, avatars, unread badge: use the same classes Steam's Friends list uses, placed in the DOM contexts Steam's CSS expects. Conversation rows follow each friend's live presence: online uses Steam's blue treatment, in-game green, offline gray, and broadcasts purple, while away friends get Steam's dimmed away styling on top. Group chats are styled like online friends, and rows whose presence hasn't resolved yet are styled as offline, so every row state maps to a real Steam style that themes already handle.
  • Row text: the name and message preview also carry the classes Steam puts on its own Friends list text, nested the way Steam nests them. Several themes color names and status text only through those inner classes, so without them the Chats tab would keep Steam's default colors under a theme that recolors everything else. The plugin strips the layout side effects of those classes and pins its own font sizes, so themes can recolor the text but not squash the two-line rows.
  • Plugin CSS is now mostly geometry: row layout, truncation, scrolling. Visuals with no suitable Steam equivalent (toolbar strip, timestamps, empty/error states) stay ours.

The plugin also detects whether a Millennium theme is active: default Steam keeps its exact original look, while themed clients get neutral translucent fallbacks for the toolbar strip and search field. The search fallback is deliberately weak, it sits before the theme's stylesheet in the cascade, so any theme that styles Steam's own search field overrides it automatically. Themes that never touch the field get a translucent neutral instead of Steam's default colors.

docs/steam-class-mapping.md documents every borrowed class with the Steam CSS rule that backs it, for future Steam-update triage.

How it was tested

  • Verified visually in default Steam and third-party themes, in both the desktop and Shift+Tab overlay Friends windows. Presence colors, away labels, group rows, and offline avatar hover behavior match their native Friends-list counterparts.
  • bun run typecheck, bun test (35 tests), and bun run build all pass.

Notes for review

  • A few native rules are deliberately overridden: the Chats tab label forces its own visibility because some themes hide native tab labels entirely, and the borrowed text classes have their sizing pinned because some themes size that text for Steam's single-line rows. docs/steam-class-mapping.md records every spot where a native rule is overridden on purpose.
  • Version bumped to 0.4.0.

Theme Screenshots

Steam Default

showcase-default

Minimal Dark

showcase-minimaldark

SpaceTheme

showcase-spacetheme

Material

showcase-material

Alex979 added 30 commits August 16, 2026 20:24
Steam themes target Friends' native class names, so the custom tab and list bypassed theme styling. Resolve unique desktop modules at runtime and keep CSS limited to layout with gated fallbacks.
Real class-map excerpts guard the desktop Friends filters against similarly named Gamepad and mini-profile modules.
CSS module filters no longer represent valid desktop styling evidence. Keep coverage on the live sibling-copy path that can genuinely fail.
Steam's literal classes depend on exact tags, attributes, ancestors, and state. Recreate only the contexts proven by desktop CSS while keeping Recent Chats geometry explicit and ungated.
Successful module resolution did not prove that desktop Friends selectors matched the injected DOM. Retain only live tab-class copying, whose missing-sibling case is observable.
Record the selectors, required DOM context, and contributed properties for every retained native class. Treat the installed stylesheet as ground truth instead of JavaScript export names.
Valve's desktop tab class has transitions but no hover rule and uses the default cursor. Add a theme-neutral animated overlay and pointer cursor without changing active-tab styling.
Themed tab rules sized the injected tab differently because it lived in a
wrapper div with its own flex context, so the tab element is now the host
itself, mounted beside the native tab with symmetric inline padding. The
toolbar background becomes a translucent neutral instead of a Steam hue,
and the list reserves a stable scrollbar gutter since themes may zero the
container's scrollbar-side padding.
Live-theme testing surfaced breakage the static CSS audit could not:
themes force row margins, collapse the labelHolder hook, hide tab labels,
and strip scrollbar-side container padding. Rows drop the labelHolder
class and explicit width, the card scrolls instead of the container, the
tab matches native content-box sizing and pins its label opacity, and the
search form borrows MemberListOptionsContainer for Valve's brighter
placeholder. Toolbar and field colors switch to translucent neutrals only
when injected theme stylesheets are detected (html.rcp-themed), keeping
default Steam pixel-faithful.
Documents the rcp-themed detection mechanism, the deliberate dual
treatment of toolbar/field colors, and the hardening lessons from live
themes behind each structural choice.
Valve's borrowed rules misbehaved in our always-open context (badge pinned
over the timestamp, #555 query text, both tabs reading active, dead
clear-button hover, global 14px scrollbar), so the plugin now overrides
those specific properties. Theme detection keys solely on Millennium's
per-document link#millennium-injected marker — weaker signals false-
positived on default Steam. Also hardened: the tab requires socialListTab
before trusting a copied class list and re-adopts a late-rendering sibling,
the state-token filter is an explicit stop-list, avatars retry after a
transient load error, and anchor discovery can no longer match the plugin's
own injected panel.
Theme-specific active states and input and row rules were still being overridden by plugin CSS. Manage native selection directly and leave themed colors to Steam while correcting unread and avatar geometry.
Dropping the themed field colors entirely left Steam's default #262930
field under themes that never style friendSearchInput. The translucent
fallback now lives in a separate sheet inserted before Millennium's
theme stylesheet: source order lets it beat Steam's defaults while any
theme rule at Steam's own specificity still wins. A :focus variant
matches Valve's focus-rule specificity so the field doesn't revert
while typing.
Recent chats previously reused online colors for every conversation. Apply Steam's live presence states while keeping group and unresolved rows neutral.
Broadcast, group, and unresolved rows lack native friend-row hover rules. Reuse Steam's neutral group-chat treatment at zero specificity so theme rules still win.
Keep group and unknown semantics in the data model while rendering them with Steam's online and offline classes. Broadcast rows now rely entirely on Steam's native styling.
Themes can scope away foregrounds to Steam's nested persona text hooks while styling the outer hover independently. Reuse only those text hooks and neutralize their native layout effects.
Theme options can restyle the reused native persona text tokens with
important font rules, shrinking only away rows. Pinning the plugin's
own typography keeps every row consistent.
Themes like SpaceTheme color names and subtext only through Steam's
nested labelHolder and persona text tokens, so away-only adoption left
the other rows on Valve defaults with a mismatched palette. All rows now
share the nested structure and the existing geometry/typography pins.
@Alex979
Alex979 marked this pull request as ready for review August 19, 2026 16:41
@Alex979
Alex979 merged commit ad6c118 into main Aug 19, 2026
@Alex979
Alex979 deleted the native-steam-classes branch August 19, 2026 16:41
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