Skip to content

feat(cockpit-render): redesign the other 5 render examples (encapsulated CSS, no Tailwind)#761

Merged
blove merged 7 commits into
mainfrom
blove/render-examples-redesign-followup
Jul 6, 2026
Merged

feat(cockpit-render): redesign the other 5 render examples (encapsulated CSS, no Tailwind)#761
blove merged 7 commits into
mainfrom
blove/render-examples-redesign-followup

Conversation

@blove

@blove blove commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #759 — applies the same encapsulated-CSS redesign to the other 5 render/* examples, eliminating all Tailwind utility classes (which don't compile for the embedded example builds) in favor of encapsulated Angular component styles on the real --ds-* design tokens.

What changed

  • Shared: moved the JSON syntax tokenizer to cockpit/render/shared/json-highlight.ts so every example reuses it (spec-rendering's import updated).
  • All 5 examples (registry, computed-functions, element-rendering, state-management, repeat-loops): render-green segmented pill tabs (replacing hardcoded indigo / themed-primary — now consistent), "Streaming" status pulse, dark syntax-colored JSON console, sr-skeleton loaders, restyled demo components — all on --ds-* tokens, zero Tailwind.
  • New consistent "controls panel" styling for the 3 examples with bespoke controls:
    • element-rendering — styled "Show Detail" checkbox (render-green accent)
    • state-management — styled Name/Age/Theme form
    • repeat-loops — "+ Add Item" button + removable list rows
  • Each example's store read/write logic preserved unchanged; only presentation restyled.
  • Added *.spec.ts build-exclude to each app's tsconfig.app.json (consistency with feat(cockpit-render): redesign spec-rendering playback UI (encapsulated CSS, no Tailwind) #759).

Verification

  • ✅ All 6 render examples build within budget (nx run-many -t build)
  • ✅ Repo-wide Tailwind-free grep across every render example component
  • ✅ 8/8 highlighter tests (new shared location)
  • ✅ Chrome MCP: all 3 controls examples verified live — checkbox toggles, form writes to the store (survives streaming), list add/remove mutates /items; segmented tabs, JSON console, status pulse all working

Plan: docs/superpowers/plans/2026-07-06-render-examples-redesign-followup.md

🤖 Generated with Claude Code

blove and others added 7 commits July 6, 2026 16:30
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d CSS, JSON console, styled Show-Detail control)
… CSS, JSON console, styled state form)

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

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadplane Ready Ready Preview, Comment Jul 6, 2026 11:48pm

Request Review

@github-actions github-actions Bot 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.

Automated approval: this PR received an intelligent (AI) code review. See the review comments on this PR.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Claude finished @blove's task in 3m 43s —— View job


Code Review

  • Gather context and understand the request
  • Read the PR diff
  • Review component files for correctness, type safety, and Angular pitfalls
  • Check shared utilities and test coverage
  • Post review findings

Overall the redesign is clean and consistent — Tailwind is fully gone, --ds-* tokens are used throughout, the shared json-highlight.ts extraction is the right call, and all the streaming/store logic is preserved intact.

Three issues worth fixing before merge:

1. DemoBadgeComponent shows an empty styled span during loading (registry.component.ts line 89)

The class still declares readonly loading = input(false) but the template was simplified to:

template: `<span class="b">{{ label() }}</span>`,

Before the label token arrives during streaming, this renders a zero-content <span> with a green border — looks like a broken pill. The old template had an @else if (loading()) skeleton branch. Either restore the skeleton or drop the dead loading input. Fix this →

2. Theme <select> lost its value reflection (state-management.component.ts line 381)

The old code had [value]="getState('/settings/theme')" on the select; the new code has only (change). Happens to work today because the initial store value 'dark' matches the first HTML option, but if the initial state or option order ever differs the select will show the wrong selection. The Angular-idiomatic fix is to bind [selected] on each option or add FormsModule with ngModel. Fix this →

3. Blinking cursor never stops (all 5 components, e.g. computed-functions.component.ts near the json__cursor class)

.json__cursor { animation: sr-blink 1s step-end infinite; }

The cursor blinks unconditionally — even after streaming is complete it still pulses, falsely signalling that output is ongoing. Add [class.json__cursor--hidden]="!simulator.playing()" (and a CSS rule { opacity: 0; }) or toggle display:none on the cursor span when !simulator.playing(). Fix this →


Minor / non-blocking:

  • @keyframes sr-pulse / sr-blink are defined inside 5 component styles blocks. Angular's emulated encapsulation does not scope @keyframes — they're emitted to the global stylesheet once per component instance. Since all definitions are identical there's no conflict, but if these components are ever on the same page simultaneously you'll get duplicate keyframe rules. Moving them to styles.css (already present per example) would fix this.
  • statusLabel() / streamStateLabel() / percent() are duplicated verbatim across all 5 components — good candidates for a shared base class or utility if this set grows.

@blove blove enabled auto-merge (squash) July 6, 2026 23:46
@blove blove merged commit d8528a9 into main Jul 6, 2026
23 checks passed
border: 1px solid rgba(53, 176, 106, 0.35);
}
`,
template: `<span class="b">{{ label() }}</span>`,

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.

The loading input (line 97) is still declared but the template now unconditionally renders the span, so an empty green-bordered pill appears while the badge label is streaming in. Restore the conditional or drop the dead input:

Suggested change
template: `<span class="b">{{ label() }}</span>`,
template: `@if (label()) {<span class="b">{{ label() }}</span>} @else if (loading()) {<div class="sr-skeleton" style="height:18px;width:4rem;display:inline-block;border-radius:999px"></div>}`,

</div>
<div class="control-row">
<span class="control-label">Theme</span>
<select class="control-select" (change)="setState('/settings/theme', $any($event.target).value)">

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.

The [value] binding present in the original was dropped. The select doesn't reflect current store state — it works now only because the initial store value 'dark' coincidentally matches the first HTML option. Bind [selected] on each option to make it correct:

Suggested change
<select class="control-select" (change)="setState('/settings/theme', $any($event.target).value)">
<select class="control-select" (change)="setState('/settings/theme', $any($event.target).value)">
<option value="dark" [selected]="getState('/settings/theme') === 'dark'">Dark</option>
<option value="light" [selected]="getState('/settings/theme') === 'light'">Light</option>
</select>

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