Skip to content

Redesign language chooser and add light/dark code theme#19940

Open
jeffmerrick wants to merge 8 commits into
masterfrom
jeffmerrick/issue-19928-update-language-chooser
Open

Redesign language chooser and add light/dark code theme#19940
jeffmerrick wants to merge 8 commits into
masterfrom
jeffmerrick/issue-19928-update-language-chooser

Conversation

@jeffmerrick

@jeffmerrick jeffmerrick commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

In addition to updates to the language chooser to make it fit more languages more easily, this updates all code blocks in /docs to be theme-aware instead of always dark mode.

TBD: what icon to use for HCL when it's added, or consider if we want language icons at all.

Follow up work: replicate these changes in the registry.

Screenshot 2026-06-29 at 2 44 42 PM Screenshot 2026-06-29 at 2 44 47 PM

Resolves #19928.

Redesigns the language chooser into a bordered card with a left-label toolbar, and makes code blocks theme-aware in /docs (a new Min Light theme for light mode alongside the existing Min Dark default).

Changes

Language chooser redesign

  • Wraps language choosers (type="language") in a bordered card with a gray toolbar: the active language's colored logo + full name on the left, underline-style abbreviation tabs (TS, PY, GO, …) on the right.
  • Tabs now show uppercased file-extension abbreviations instead of full names, so 7+ language options fit on one row.
  • Adds a logo (and optional logoOnDark) field to each SupportedLanguage; YAML ships a dark-mode wordmark variant swapped via the docs .docs-logo-light/.docs-logo-dark classes.
  • Example-program choosers (code + GitHub footer) run flush to the card edges instead of double-padding; loose prose stays uniformly inset.
  • Headless/inline choosers (option-style="none") keep no chrome — the toolbar wrapper is hidden alongside the list.
  • Updates the C# tech logo SVG to the full-color C# diamond.

Theme-aware code blocks (/docs)

  • Adds theme/src/scss/docs/_code-light.scss — the Min Light code theme (Violet 50 surface) for docs light mode, mirroring the Min Dark dark-mode block. Comments are bumped for contrast; everything else is the canonical Min Light palette.
  • Imported unlayered from main.scss so it beats the layered component rules, scoped to html:not([data-theme="dark"]) body.section-docs so it can't touch non-docs pages or dark mode.

Code-panel chrome refinements

  • Copy button is now a transparent ghost chip with a single muted foreground that reads on both the dark panel and the docs-light surface (removes the old code-mode-dark overrides).
  • Copy-button and example-program footer tooltips now point left of their trigger (they sit at the panel's top/bottom-right corners where an upward tooltip would overflow).
  • Example-program footer matches the code block's background as its bottom lip in both modes; GitHub icon shrunk.
  • Note/callout icon styling scoped to div.icon-and-line so it no longer leaks onto copy-button icons.

Tests

  • Adds e2e coverage for the new language toolbar (logo label + abbreviated tabs) and confirms non-language choosers still render plain full-name tabs with no toolbar.

jeffmerrick and others added 3 commits June 29, 2026 13:51
Convert the type="language" pulumi-chooser into a bordered card: a gray
top toolbar showing the active language's colored logo + full name on the
left and full-height underline-style abbreviation tabs (TS, PY, GO, …) on
the right, with the slotted code/prose below. Scoped to non-headless
language choosers; other chooser types and inline/headless choosers are
unchanged.

- chooser.tsx: add per-language `logo`, render the toolbar + abbreviation
  tabs for language, helper for tab labels; extend e2e coverage.
- _chooser.scss: card + toolbar styling via semantic --docs-* tokens
  (dark mode for free); collapse code-unit margins at choosable edges
  (.highlight / .example-program) and drop top margin after a p/ul/ol;
  flush example-program panels.
- _docs-theme.scss: dark overrides for the new toolbar tabs (the unlayered
  `*` border reset would otherwise win) and scope note status-icon color
  to .icon-and-line so copy buttons aren't tinted.
- _copy-button.scss: match the code block background with a gray outline.
- _notes.scss: scope note icon color/size to .icon-and-line; scope the
  last-child padding reset to content blocks so copy buttons aren't hit.
- _code.scss: example-program footer uses the code background, fix the
  GitHub tooltip positioning and shrink its icon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add Min Light code theme for /docs light mode (theme/src/scss/docs/_code-light.scss),
  scoped to html:not([data-theme="dark"]) body.section-docs; Min Dark stays the
  default everywhere else and in docs dark mode. Per the Pulumi brand "Code
  highlighting" guideline (Min Theme on Violet 50 / Violet 950).
- Consolidate the code copy button into one predictable ghost-chip style and
  drop the obsolete dark-mode violet-950 override.
- Position the copy-button and example-program "View full example" tooltips to
  the left of their trigger instead of above.
- Replace the C# language logo with the canonical purple hex mark.
- Make the chooser language logos dark-mode aware (optional logoOnDark +
  .docs-logo-light/.docs-logo-dark swap); use the existing matched dark YAML
  asset and the mark-only Java cup logo.
- Add an optional Preview badge next to the active label in the chooser toolbar.
- Chooser plain tabs: use --docs-surface (inactive) / --docs-border (hover)
  borders and service-black active label text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The chooser renders options in the declaration order of the supported*
arrays (mapOptions filters them, preserving source order), not the order
passed in the options attribute. Correct both new assertions to expect
the actual render order.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeffmerrick jeffmerrick marked this pull request as ready for review June 29, 2026 19:46
@jeffmerrick jeffmerrick requested a review from cnunciato June 29, 2026 19:46
@github-actions github-actions Bot added the review:triaging Claude Triage is currently classifying the PR label Jun 29, 2026
@jeffmerrick jeffmerrick requested a review from CamSoper June 29, 2026 19:46
@github-actions github-actions Bot added domain:infra PR touches workflows, scripts, infra, Makefile, or build config review:in-progress Claude review is currently running and removed review:triaging Claude Triage is currently classifying the PR labels Jun 29, 2026
@pulumi-bot

pulumi-bot commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

@github-actions

Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-06-29T19:48:31Z

Tip

Summary: This PR redesigns the docs language chooser and adds a light/dark code-highlighting theme. It restyles the chooser into a bordered card with a logo-labeled toolbar and abbreviated language tabs (a Stencil component change plus accompanying SCSS), adds a Min Light Chroma palette gated to /docs light mode, consolidates the copy-button and code-footer styling across both themes, and replaces the C# tech logos with the official C# mark. The wrongness that would matter here is a CSS/Stencil regression — broken tab rendering, an unreadable code panel in one theme, or a tooltip that overflows the panel — which a static diff read can't fully rule out (SCSS/Stencil compile, asset bundling, and the Hugo build run in their own CI jobs). Reviewed against the infra rubric: no secrets, no workflow/dependency/bundling changes, and no clearly-broken state; the updated e2e assertions match the documented render order. The claim-extraction pass surfaced 20 candidates, all of which are CSS values, SVG geometry, code comments, or internal asset paths — none are falsifiable external claims.

Review confidence:

Dimension Level Notes
mechanics MEDIUM reviewed statically; SCSS/Stencil compile, asset bundling, and the Hugo build run in their own CI jobs and weren't executed here
facts n/a infra/theme change — no falsifiable claims; all 20 extracted candidates were CSS values, SVG geometry, or asset paths
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 0 of 20 claims verified (0 unverifiable, 0 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 0 Pass 1, 0 Pass 2, 20 Pass 3 (verified 0, contradicted 0, unverifiable 20).
  • Cited-claim spot-checks: not run (no cited claims)
  • Frontmatter sweep: not run (no frontmatter in diff)
  • Temporal-trigger sweep: ran (recency words present in diff; spot-check in-review)
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: not run (no fenced code blocks in content files)
  • Editorial-balance pass: not run (not under content/blog/)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
0 0 0 0

🔍 Verification trail

20 claims extracted · 0 verified · 0 unverifiable · 0 contradicted
  • L2 in static/logos/tech/c-sharp.svg "<svg width='256px' height='288px' viewBox='0 0 256 288' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' preserveAsp…" → ➖ not-a-claim (evidence: The "claim" is an SVG element attribute line referencing the standard W3C SVG namespace URI (http://www.w3.org/2000/svg), which is a fixed XML namespace identifier, not a falsifiable assertion about external content.; source: http://www.w3.org/2000/svg)
  • L4 in static/logos/tech/c-sharp.svg "<path d='M255.569,84.452376 C255.567,79.622376 254.534,75.354376 252.445,71.691376 C250.393,68.089376 247.32,65.070376 243.198,62.683376 C209.173,43.064376 175…" → ➖ not-a-claim (evidence: The line is raw SVG path data (d="M255.569,84.452376 ...") inside a logo file. It contains numerical coordinates that are part of a vector graphic shape definition, not a falsifiable factual assertion about the world. The `found_by: rege…; source: static/logos/tech/c-sharp.svg L4 — SVG path geometry data)
  • L2 in static/logos/tech/csharp.svg "<svg width='256px' height='288px' viewBox='0 0 256 288' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' preserveAsp…" → ➖ not-a-claim (evidence: The "source" here is the standard W3C SVG namespace URI http://www.w3.org/2000/svg, which appears as an XML namespace attribute value in an SVG file — it is a well-known, stable namespace identifier, not a falsifiable factual claim made…; source: http://www.w3.org/2000/svg)
  • L4 in static/logos/tech/csharp.svg "<path d='M255.569,84.452376 C255.567,79.622376 254.534,75.354376 252.445,71.691376 C250.393,68.089376 247.32,65.070376 243.198,62.683376 C209.173,43.064376 175…" → ➖ not-a-claim (evidence: The "claim" is raw SVG path data (d="M255.569,84.452376 ...") from a logo file. SVG path coordinates are geometric drawing instructions, not falsifiable factual assertions about the world. The type: numerical label refers to numeric co…; source: static/logos/tech/csharp.svg L4 — SVG path element)
  • L261 in theme/src/scss/_code.scss "top: 50%;" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (top: 50%) in a stylesheet. This is a design/layout decision made by the PR author in their own code, not a falsifiable assertion about a third-party fact. CSS percentage values in positioning context…; source: repo:theme/src/scss/_code.scss L261)
  • L264 in theme/src/scss/_code.scss "right: 100%;" → ➖ not-a-claim (evidence: The text "right: 100%;" is a CSS property-value declaration in a stylesheet, not a falsifiable factual assertion. It is a design/styling choice made by the PR author in their own code, not a third-party-attributed claim about the world.; source: theme/src/scss/_code.scss L264)
  • L266 in theme/src/scss/_code.scss "transform: translateY(-50%);" → ➖ not-a-claim (evidence: The text "transform: translateY(-50%);" is a CSS property value in a stylesheet, not a falsifiable factual assertion. It is a design/implementation detail authored by the PR author in their own code file, not a third-party-attributed claim.; source: repo:theme/src/scss/_code.scss L266)
  • L271 in theme/src/scss/_code.scss "top: 50%;" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (top: 50%) in a SCSS stylesheet. This is a styling/layout declaration, not a falsifiable factual assertion about a third-party system, API, price, or capability. It is the PR author's own design choice.; source: repo:theme/src/scss/_code.scss L271)
  • L274 in theme/src/scss/_code.scss "transform: translateY(-50%) rotate(45deg);" → ➖ not-a-claim (evidence: The text "transform: translateY(-50%) rotate(45deg);" is a CSS property value in a stylesheet file. It is not a falsifiable factual assertion about the world — it is the PR author's own design/styling code. The numerical values (-50%, 45de…; source: repo:theme/src/scss/_code.scss L274)
  • L278 in theme/src/scss/_code.scss "// The global active rule re-introduces bottom: 100%; cancel it so the" → ➖ not-a-claim (evidence: The line is a CSS code comment in a SCSS file describing an internal style override ("cancel it so the…"). It is not a falsifiable assertion about a third-party fact, API, price, or capability — it is the PR author's own description of the…; source: repo:theme/src/scss/_code.scss L278)
  • L23 in theme/src/scss/_copy-button.scss "background: color-mix(in oklch, var(--color-white) 8%, transparent);" → ➖ not-a-claim (evidence: The line background: color-mix(in oklch, var(--color-white) 8%, transparent); is a CSS property value authored by the PR author in their own SCSS file. It is a design/implementation choice, not a falsifiable assertion about a third-party…; source: WebSearch ran query "CSS color-mix oklch white 8% transparent background button"; MDN docs at https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix)
  • L39 in theme/src/scss/_copy-button.scss "top: 50%;" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (top: 50%) in a stylesheet file. This is a design/layout decision made by the PR author in their own code, not a falsifiable assertion about a third-party fact. The 50% here is a CSS positioning valu…; source: theme/src/scss/_copy-button.scss L39)
  • L42 in theme/src/scss/_copy-button.scss "right: 100%;" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (right: 100%;) in a SCSS file. This is a styling rule authored by the PR author themselves — it is not a falsifiable assertion about a third-party fact, version, price, or capability. It is a design/la…; source: repo:theme/src/scss/_copy-button.scss L42)
  • L44 in theme/src/scss/_copy-button.scss "transform: translateY(-50%);" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (transform: translateY(-50%)) found in a SCSS file. This is a standard CSS transform declaration, not a falsifiable factual assertion about a third-party system, API, or documented behavior — it is the…; source: repo:theme/src/scss/_copy-button.scss L44)
  • L49 in theme/src/scss/_copy-button.scss "top: 50%;" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (top: 50%) in a stylesheet file. This is a design/layout decision made by the PR author in their own code, not a falsifiable assertion about a third-party fact. A CSS percentage value in a .scss file…; source: repo:theme/src/scss/_copy-button.scss L49)
  • L52 in theme/src/scss/_copy-button.scss "transform: translateY(-50%) rotate(45deg);" → ➖ not-a-claim (evidence: The "claim" is a CSS property value (transform: translateY(-50%) rotate(45deg);) found in a SCSS file. This is a styling rule in the PR author's own design/code, not a falsifiable assertion about a third-party fact. The numerical values…; source: repo:theme/src/scss/_copy-button.scss L52)
  • L56 in theme/src/scss/_copy-button.scss "// The global active rule re-introduces bottom: 100%; cancel it so the" → ➖ not-a-claim (evidence: The line is a CSS code comment in a SCSS file describing the author's own design intent ("cancel it so the…"). It contains no falsifiable third-party assertion — the "100%" is a CSS property value referenced in a comment, not a numerical c…; source: repo:theme/src/scss/_copy-button.scss L56)
  • L539 in theme/stencil/src/components/chooser/chooser.tsx "logo: '/images/docs/icons/languages/java-color-32-32.svg'," → ➖ not-a-claim (evidence: The line logo: "/images/docs/icons/languages/java-color-32-32.svg" is an internal asset path string within the PR author's own codebase (chooser.tsx in the pulumi/theme or pulumi-hugo repo). It is a faithful description of the author's o…; source: WebSearch ran query "github pulumi/pulumi-hugo chooser.tsx java-color-32-32.svg"; top results confirmed the file lives in pulumi/theme (now pulumi-hugo) but did not surface the specific line.)
  • L547 in theme/stencil/src/components/chooser/chooser.tsx "// div.yaml-color-32-32 icon); the -on-dark recolor keeps the black" → ➖ not-a-claim (evidence: The text is a code comment in a TSX source file describing a CSS class name and a visual recolor behavior. It contains no falsifiable assertion about a third-party system, API, price, version, or documented fact — it is purely an inline im…; source: repo:theme/stencil/src/components/chooser/chooser.tsx L547)
  • L549-550 in theme/stencil/src/components/chooser/chooser.tsx "logoOnDark: '/images/docs/icons/languages/yaml-color-32-32-on-dark.svg'," → ➖ not-a-claim (evidence: The line logoOnDark: "/images/docs/icons/languages/yaml-color-32-32-on-dark.svg" is an internal static asset path defined in the PR author's own chooser component. It describes the PR's own design (icon mapping for YAML), not a third-par…; source: WebSearch ran query "pulumi docs yaml-color-32-32-on-dark.svg chooser languages icon"; top results didn't address the claim; intuition: The 32-32 in the filename encodes pixel dimensions (32×32 px), consistent with the naming convention used for other l…)

🚨 Outstanding in this PR

No outstanding findings in this PR.

⚠️ Low-confidence

No low-confidence findings.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues in touched files.

✅ Resolved since last review

No items resolved since the last review.

📜 Review history

  • 2026-06-29T19:48:31Z — Infra/theme PR (chooser redesign + light/dark code theme); static review found no blockers — no secrets, workflow, or dependency changes, and all 20 extracted candidates were non-claims (d7408a5)

Need a re-review? Want to dispute a finding? Mention @claude and include #update-review.
(For ad-hoc questions or fixes, just @claude — no hashtag.)

@github-actions github-actions Bot added review:no-blockers Claude review completed cleanly; outstanding is empty and removed review:in-progress Claude review is currently running labels Jun 29, 2026
@pulumi-bot

pulumi-bot commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Lighthouse Performance Report

Commit: 4ff5aab | Metric definitions

Page Device Score FCP LCP TBT CLS SI
Homepage Mobile 🔴 49 3.2s 3.7s 1894ms 0.012 7.5s
Homepage Desktop 🟡 78 0.8s 1.1s 307ms 0.040 2.8s
Install Pulumi Mobile 🟡 58 5.5s 8.2s 50ms 0.057 8.0s
Install Pulumi Desktop 🟡 85 1.3s 1.9s 0ms 0.005 1.8s
AWS Get Started Mobile 🟡 57 5.4s 8.0s 54ms 0.088 7.9s
AWS Get Started Desktop 🟡 81 1.4s 1.9s 0ms 0.023 3.0s

- Add a `position="left"` prop to pulumi-tooltip (placement lives in the
  component + _tooltip.scss); opt in from the copy button and example-program
  footer, removing the per-consumer positioning overrides.
- Source all chooser language logos from /images/docs/icons/languages for
  consistency (copy in typescript/javascript/csharp/fsharp/visualbasic/opa;
  switch python/go); keep the C# hex distinct from the .NET mark.
- Trim overly verbose comments across the branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention and removed review:no-blockers Claude review completed cleanly; outstanding is empty labels Jun 29, 2026

@CamSoper CamSoper left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I love it! You're a rockstar, Jeff.

- Add "hcl" to the chooser's LanguageKey and supportedLanguages (marked
  preview: true), with a compact braces icon (hcl-color-32-32.svg); keep the
  full "HCL" wordmark as hcl-wordmark-color-32-32.svg.
- Show the Preview badge only on the toolbar label for language choosers, not
  on the tabs (other chooser types keep the tab badge).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cnunciato cnunciato left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ok so first off, this looks fantastic. I love it. ✨

Still poking, but wanted to get you a first pass to work with some of the more noticeable things I've seen so far.

  1. We use choosers to toggle blocks of content on the page, which currently produces what looks like an empty container (new vs. current):

    image
  2. Variations on the chooser look different. Should we change these too, or leave them the same?

    image
  3. Many have extra padding — might be a quirk of Hugo rendering?

    image
  4. I assume we're pulling this from the extension, but it'd be great to have the label here be C# rather than CS:

    image
  5. The wrapping is a bit awkward on mobile. Should we maybe hide the full name and the icon below a certain width? 🤔

    image

jeffmerrick and others added 2 commits July 9, 2026 15:56
…g fix

- Standalone (self-closing) language choosers render as a toolbar strip
  instead of an empty card; generic (os/persona/etc.) choosers restyled
  to match (surface bg, small tabs, violet active underline)
- Code-only choosers run their single code block flush to the card edges
- C# and F# tabs use their proper names via a tabLabel override
- Mobile: toolbar label hidden, tab row scrolls horizontally
- Fix the chooser global-reset bug: a chooser missing the preferred
  option no longer dispatches a page-wide language reset (which made
  YAML unselectable on pages like /docs/iac/concepts/stacks/). It now
  shows a display-only fallback, keeps following the store, and adopts
  the sibling choosables it controls so their content stays visible
- Wrap the Stack README chooser in stacks.md so it renders as a card

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeffmerrick

Copy link
Copy Markdown
Contributor Author

@cnunciato Good catches and feedback, thank you!

  1. Fixed with an alternate style for the standalone ones.
Screenshot 2026-07-09 at 3 59 23 PM
  1. I've updated these, the only downside is they can be used elsewhere on the marketing site and look a little weird there, but looks like currently only on /solutions (which probably could use some TLC anyhow.)
Screenshot 2026-07-09 at 3 59 30 PM
  1. This was by design since they can contain a single code block or several code blocks plus text. BUT, I've made a change so if there is a single code block it will be flush.
Screenshot 2026-07-09 at 4 00 00 PM
  1. Done! (For F# too.)
  2. Fixed!
Screenshot 2026-07-09 at 4 02 49 PM

Unrelated update: I changed the info notes back to blue instead of violet - since the code background is violet in light mode now they were really blending in with eachother too much.

Also bonus: ran across and fixed a bug where tabs weren't working when there were mixed chooser options (clicking YAML here https://www.pulumi.com/docs/iac/concepts/stacks/#outputs doesn't work.)

@cnunciato cnunciato left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks great! Will keep an eye out for any additional issues, but so far, everything looks solid. Well done!

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

Labels

domain:infra PR touches workflows, scripts, infra, Makefile, or build config review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update language chooser to handle more languages

4 participants