Skip to content

Revamped the remaining UI refresh updates - #287

Open
Kush172005 wants to merge 3 commits into
opencost:mainfrom
Kush172005:ui/modernize-opencost-ui-phase-2
Open

Revamped the remaining UI refresh updates#287
Kush172005 wants to merge 3 commits into
opencost:mainfrom
Kush172005:ui/modernize-opencost-ui-phase-2

Conversation

@Kush172005

Copy link
Copy Markdown
Contributor

Summary

This PR completes the remaining UI refresh work following the initial UI modernization PR.

The focus of this update is to bring the remaining components in line with the new design system, improve visual consistency across the application, and address the remaining light and dark theme inconsistencies. As with the previous PR, no application functionality, APIs, business logic, or workflows have been changed.

What's changed

  • Updated the remaining modal dialogs to match the new UI design.
  • Improved Carbon modal styling with consistent spacing, borders, shadows, and backdrop effects.
  • Refined buttons, inputs, text areas, selects, tiles, and tags for a more consistent appearance.
  • Improved focus states and input styling across the application.
  • Fixed dark mode inconsistencies in input fields and form controls.
  • Updated legacy Material UI dialogs to match the refreshed design system.
  • Improved responsive behavior for modal dialogs across different screen sizes.
  • Applied the new design system consistently across the remaining UI components.

What hasn't changed

  • No backend changes.
  • No API changes.
  • No business logic changes.
  • No routing changes.
  • No changes to existing workflows or user interactions.

This PR completes the remaining UI updates while maintaining the same functionality and user experience introduced in the previous refresh as discussed.

Testing

  • Verified existing functionality after the UI updates.
  • Tested both White and g100 (Dark) themes.
  • Verified responsive layouts across supported screen sizes.
  • Tested updated modals, dialogs, buttons, inputs, selects, and form interactions.
  • Confirmed existing functionality and workflows continue to work as expected.

Signed-off-by: Kush Agarwal <agrawalkush783@gmail.com>
@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for opencost-ui ready!

Name Link
🔨 Latest commit 16e9498
🔍 Latest deploy log https://app.netlify.com/projects/opencost-ui/deploys/6a89c3ea32b0b40008ca39ef
😎 Deploy Preview https://deploy-preview-287--opencost-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Kush172005

Copy link
Copy Markdown
Contributor Author

cc @ameijer
done with revamping the remaining components. Have a look when you get a chance.
Thanks!

@peatey

peatey commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review: PR #287 — revamped remaining UI refresh updates

Thanks for the refresh — the modal styling direction looks good. The main structural issue is that most of the new rules in app.scss are global !important overrides on Carbon classes (.cds--tile, .cds--select-input, .cds--tag, .cds--label, .cds--text-input, .cds--modal-footer .cds--btn), and they leak well beyond the modals this PR is styling — several primary pages regress. Scoping these rules under the specific modal containers (or using Carbon tokens/props) would resolve the majority of the findings below in one move.

Regressions introduced by this PR

app.scss

1. .cds--tile override kills the dashboard builder's state indicators (app.scss:661)
background-color: var(--cds-layer) !important; border: 1px solid var(--cds-border-subtle) !important beats the inline styles dashboard-builder.tsx uses as functional state: the selected-widget 2px solid var(--cds-focus) border (dashboard-builder.tsx:147-152) and the dashed "Add Widget" border (line 197) both flatten to a plain 1px border, so clicking a widget no longer shows which one is selected. The background also defeats bg-[var(--cds-layer-accent)] on the Configure Widget panel (line 213) and external-services-chart-widget.tsx:238 (Tailwind utilities are non-important and previously won by source order).

2. Focus outlines stripped app-wide, replacement fails WCAG (app.scss:598-603, 623-628, 653-657)
Three blocks apply outline: none !important, removing Carbon's 2px focus outline (#0f62fe on white, #ffffff on g100 — ~16:1 contrast) everywhere. The replacement box-shadow: 0 0 0 2px rgba(15,98,254,0.2) composites to ~1.34:1 on white and ~1.2:1 on g100's #262626 fields (the rgba is hardcoded to the light theme's blue) — both far below WCAG 1.4.11's 3:1 minimum. In Windows High Contrast / forced-colors mode, box-shadows are suppressed while outline: none is honored, so focus becomes completely invisible. Carbon's prefers-contrast dotted fallback and the red invalid-state outline (while focused) are also discarded. All three blocks need fixing independently.

3. .cds--select-input override defeats size="sm" and restyles Pagination (app.scss:612-629)
height: 2.5rem !important beats Carbon's block-size: layout.size('height'), so the nine size="sm" filter selects in scoped-views.tsx (223/234/245, 308/319/330, 379/390/401) render 40px instead of 32px and misalign with the toolbar. The same block also forces a boxed field (border, radius, field background) onto Carbon Pagination's intentionally chrome-less inline selects (cost-allocation-table.tsx:525, cloud-cost-widget.tsx:539), and background-color !important kills their hover feedback.

4. .cds--tag padding deforms dismissible filter tags (app.scss:637-645)
padding: 0.25rem 0.75rem !important; height/min-height: auto !important overrides Carbon's .cds--tag--filter { padding-block: 0; padding-inline-end: 0 }, which exists so the circular close button sits flush at the pill's end. Filter tags get 12px of dead space after the X and 4px extra vertical padding, and the button's circular hover target/focus ring float misaligned. Hit by: cloud-cost-widget.tsx:419, cloud-cost-table-widget.tsx:207, create-report-modal.tsx:186, create-dashboard-modal.tsx:163.

5. Modal footer button height loses to Carbon's padding (app.scss:569-574)
height: 2.25rem !important on .cds--modal-footer .cds--btn is smaller than Carbon's untouched padding-block: calc(1rem - 2px) 2rem (46px, _modal.scss:338-340). With border-box sizing the content box floors at 0 and the button renders ~46-48px with the label pinned ~14px from the top — visibly top-aligned, not the intended 36px. The padding must be reset for the forced height to take effect.

6. .cds--modal transition line is a dead no-op (app.scss:520)
Carbon's fade rules land on .cds--modal:not(.cds--modal--enable-presence) (specificity 0,2,0), which beats this 0,1,0 selector without !important — Carbon's own 240ms transition continues to govern every modal and the intended 200ms fade never applies (unlike the sibling background-color line, which carries !important). Either delete it or give it the specificity it needs — in which case note that replacing Carbon's transition list drops the visibility 0ms linear 240ms delay that keeps the exit fade visible.

7. input[type="text"] selector misses the PR's own inputs (app.scss:648-658)
The new native-control focus block targets input[type="text"], but the two inputs this PR's .tsx changes touch — the tag input (create-report-modal.tsx:165-177) and the filter value input (report-filter-value-input.tsx:125-137) — have no type attribute, so the attribute selector doesn't match them. They keep the default browser outline while typed inputs elsewhere and the sibling selects in the same modal get the new blue treatment.

8. Latent: !important rules defeat Carbon's disabled/invalid state styling (app.scss:481, 586-604, 612-634)
The broadened global .cds--label rule plus the input/select blocks override Carbon's entirely non-important disabled styling (label color, transparent bottom border, dimmed chevron), and padding: 0 0.75rem !important on .cds--text-input overrides the 2.5rem end padding reserved for the invalid/warning icon, which would sit on top of the value text. No component currently passes disabled/invalid/warn, but these are global rules — the first future use gets silently wrong state styling.

9. Dead duplicate blocks (app.scss:486-495 vs 612-634)
The new .cds--select-input/.cds--select__arrow blocks fully supersede the earlier "Dark Mode Targeted Fixes" blocks for the same selectors (same specificity, both !important, later wins), leaving the earlier blocks dead and disagreeing on values (var(--cds-field) vs var(--cds-field, var(--cds-layer))). They should be merged into one rule.

Modal components

10. New close buttons have no accessible name (cloudCostDetails.jsx:161, externalCostDetailModal.jsx:74)
Both newly added close buttons contain only &times; — screen readers announce "times, button" with no indication it dismisses the dialog. Needs aria-label="Close".

11. Rebuilt modals lack dialog semantics (cloudCostDetails.jsx:143, externalCostDetailModal.jsx:69)
MUI Modal's root is role="presentation" and the Paper receiving focus has no role="dialog", aria-modal, or aria-labelledby tying it to the new visible h3 titles. Since this PR rebuilt both modals' chrome by hand, adding role="dialog" aria-modal="true" aria-labelledby={...} on the Paper would complete the job.

12. Recharts tooltip is white-on-white in dark mode (cloudCostDetails.jsx:123, 220-224)
The PR's paperStyle sets color: var(--cds-text-primary) (#f4f4f4 in g100), which the Recharts default tooltip label inherits — on Recharts' hardcoded #fff tooltip background, the label is invisible in dark mode. The Bar fill is also hardcoded light-palette #2196f3 (line 219). Passing contentStyle/labelStyle using Carbon tokens (or a themed tooltip) would fix it.

13. Leftover title props now render browser tooltips over the modals (externalCostDetailModal.jsx:66, cloudCostDetails.jsx:140)
title={...} on MUI Modal isn't a heading API — it spreads to the root div as a native HTML title attribute, so hovering the open modal pops a browser tooltip repeating the text of the h3 headers this PR added. The props should be removed now that real headers exist.

14. Modal chrome is copy-pasted between the two components (cloudCostDetails.jsx:115-182 vs externalCostDetailModal.jsx:10-95)
Near-identical style objects + header/close-button JSX (same hover handlers) in both files, and in cloudCostDetails the style objects are recreated on every render while its twin hoists them to module scope. Worth extracting a shared themed-modal header, or at least hoisting to module scope.

Pre-existing bugs in touched code (worth fixing while here)

  • cloudCostDetails.jsx:56match.length throws when the regex doesn't match (String.match returns null; the regex requires a decimal, so "ETL is 85% complete" doesn't match). Should be if (match && match.length > 0).
  • cloudCostDetails.jsx:70err.message.indexOf("404") === 0 never matches axios's "Request failed with status code 404", so the "Please update OpenCost" guidance is unreachable. Use err.response?.status === 404 or includes("404").
  • cloudCostDetails.jsx:200{data && (...)} is always truthy (data is always an array), so an empty axes-only chart renders during loading and on error. Should be data.length > 0.
  • report-filter-value-input.tsx:143 — the autocomplete <ul> has z-10/shadow-md and a relative parent but is missing absolute, so the open listbox renders in-flow and shoves the layout down by up to 12rem instead of overlaying.

The two one-line fixes

The background fixes in create-report-modal.tsx and report-filter-value-input.tsx are correct as they stand. (Also checked: backdrop-filter needs no -webkit- prefix here — the production build's css target already emits it.)

…bugs

app.scss:
- Drop !important from tile/input/select/tag/label overrides so inline
  styles, Tailwind utilities, and Carbon state styling (disabled,
  invalid, size variants) win again; the sheet compiles after Carbon so
  source order already beats Carbon's base rules
- Scope select-input styling to exclude inline (Pagination) and disabled
  selects, and stop forcing height so size="sm" works
- Exclude dismissible filter tags from the tag geometry override
- Restore accessible 2px focus outlines (var(--cds-focus)) instead of
  outline: none on Carbon and native form controls
- Reserve icon space on invalid/warning text inputs
- Reset Carbon's footer-button block padding and baseline alignment so
  the 2.25rem footer buttons center their labels
- Remove dead .cds--modal transition line and the superseded dark-mode
  select fix blocks

Modal components:
- Extract shared ThemedModalHeader + modal/paper styles used by the
  cloud cost drilldown and external cost details modals
- Add dialog semantics (role, aria-modal, aria-labelledby) and an
  accessible close-button label; drop leftover MUI Modal title props
  that rendered browser tooltips
- Theme the recharts tooltip so it stays readable in dark mode
- Fix null-unsafe regex match, dead 404 check, and always-truthy chart
  guard in cloudCostDetails

Misc:
- Add type="text" to the untyped tag/filter inputs so the native focus
  rule matches them
- Position the filter autocomplete listbox absolutely so it overlays
  instead of pushing the layout

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVGrgsSb6xR7FTRyAtmHG3
Signed-off-by: Warwick Peatey <warwick.peatey@ibm.com>
Kush172005 added a commit to Kush172005/opencost-ui that referenced this pull request Aug 22, 2026
Signed-off-by: Kush Agarwal <agrawalkush783@gmail.com>
@Kush172005
Kush172005 force-pushed the ui/modernize-opencost-ui-phase-2 branch from d02837e to 16e9498 Compare August 22, 2026 15:44
@github-actions

Copy link
Copy Markdown

Lighthouse Report

Category Score
Performance 95
Accessibility 96
Best Practices 100
SEO 83

Full Report

@peatey

peatey commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

thanks @Kush172005 we'll discuss this in Thursdays meeting! @ameijer FYI

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants