Skip to content

fix(ui): keep component tabs and use-package tabs reachable on mobile in minimal mode - #10607

Open
luvkapur wants to merge 5 commits into
masterfrom
fix/mobile-friendly-workspace-ui
Open

fix(ui): keep component tabs and use-package tabs reachable on mobile in minimal mode#10607
luvkapur wants to merge 5 commits into
masterfrom
fix/mobile-friendly-workspace-ui

Conversation

@luvkapur

@luvkapur luvkapur commented Aug 12, 2026

Copy link
Copy Markdown
Member

On phone-width viewports in minimal mode (the workspace UI embedded by workspaces.bit.cloud), the component page is a dead end: the top navigation renders no tabs at all, so there is no way to reach the Preview, and the "Use package" section shows a lone "…" tab in place of "Modify component".

Why the tabs disappear

Both tab rows are rendered by ResponsiveNavbar, which hides any tab that does not fit its container and reserves room for its "…" overflow button. The width check is conservative — after measuring every tab it still requires a full button-width of slack — so a tab row that fits exactly gets collapsed anyway.

The component menu compounds this. The top bar packs breadcrumb, nav tabs, pinned widgets, and the version dropdown into one 46px row, and only the nav can shrink (flex-grow: 1; min-width: 0). At 390px the nav is squeezed to nothing, every tab is marked hidden, and the "…" dropdown that would hold them is itself clipped out of view.

The fix

Component menu (minimal mode, ≤768px). The menu bar switches to display: contents, promoting its children into the workspace top bar's now-wrapping flex row. The first row keeps the back button, breadcrumb, pinned widgets, and right-side actions; the nav tabs wrap to a second full-width row where all of them (Overview / Preview / Graph / API Reference) fit. Two details this surfaced: the pinned widgets carry an inline height: 100% that resolves against the two-row bar and inflated the first row until the nav row was pushed out over the page content (pinned to the bar height), and a long component path now clips instead of pushing the actions off-screen.

Use-package tabs (≤768px). The nav is sized to its content (min-width: max-content) inside a wrapper that scrolls horizontally, which makes the width check always pass, and the overflow button is dropped so its reserved width can no longer hide the last tab. Both tabs render; a hypothetical wider set would scroll.

The workspace-overview filter layout from the same report is already handled on master (cd68bda stacks the filter clusters below 720px); the report came from an older release.

Testing

  • reproduced and verified against a local workspace served by bit start --dev, screenshotted headlessly at 390×844 (iPhone), 780px, and 1440px desktop, in and out of minimal mode
  • tapping Preview on the 390px minimal component page navigates to ~compositions and renders
  • 780px minimal (single-row bar) and 1440px desktop are pixel-identical to before — every change is behind max-width media queries, and the menu changes are additionally behind the minimal-mode class
  • npm run lint green

Also here: the empty-workspace state

On the same viewports, the blank state ("Your workspace is ready for its first component") pushed its second CLI card past the viewport edge. The DIY grid used bare 1fr 1fr columns, and 1fr has an auto minimum — the nowrap command text set the first column's minimum width and shoved the "Import" card off-screen. The grid now uses repeat(auto-fit, minmax(280px, 1fr)) (the width at which a full command plus its Copy button fit), so the cards sit side by side when there is room and stack when there is not; the 44px serif headline and the body padding also step down below 480px. Verified empty-workspace rendering at 360, 390, 600 (cards stacked, commands fully readable) and 1440 (unchanged two-column layout).

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix mobile minimal-mode workspace tabs becoming unreachable

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Prevent component top navigation tabs from collapsing/clipping in minimal mode on mobile.
• Make “Use package” tabs horizontally scrollable and stop overflow menu hiding the last tab.
• Adjust workspace top bar wrapping/clipping so breadcrumbs and actions remain usable at small
 widths.
Diagram

graph TD
  A["Component page (minimal)"] --> B["Workspace Top Bar"] --> C["ComponentMenu"] --> D["Nav tabs row"] --> E["ResponsiveNavbar"] --> F["Mobile CSS overrides"]
  A --> G["Use-package tabs"] --> E
  F --> B
  F --> D
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Fix ResponsiveNavbar measurement/overflow logic globally
  • ➕ Addresses root cause (conservative width slack + overflow reservation) for all tab rows
  • ➕ Reduces need for per-screen CSS overrides/hacks
  • ➕ More predictable behavior across layouts and breakpoints
  • ➖ Higher regression risk across all products using ResponsiveNavbar
  • ➖ Likely requires more extensive testing and edge-case handling (fonts, async layout, dynamic tabs)
2. Add a ResponsiveNavbar mode: “scroll instead of overflow”
  • ➕ Keeps behavior explicit and reusable (opt-in per usage)
  • ➕ Avoids hiding tabs entirely; no need to remove overflow button via CSS selectors
  • ➖ Requires component API/design change and migration across call sites
  • ➖ Still needs careful styling/UX decisions for scroll affordances
3. Move top-bar layout responsibility into WorkspaceTopBar (component slotting)
  • ➕ Centralizes wrapping/ordering rules instead of relying on display: contents
  • ➕ Can provide a stable two-row layout contract for embedded/minimal mode
  • ➖ More refactor than fix; touches broader layout architecture
  • ➖ May be harder to roll out quickly compared to CSS-scoped changes

Recommendation: The PR’s scoped, breakpoint-gated CSS approach is appropriate for a fast, low-risk fix to a specific minimal-mode mobile dead-end. If similar overflow/clipping issues recur elsewhere, consider introducing an explicit “scroll tabs” option (or a less conservative width calculation) in ResponsiveNavbar to avoid repeating per-page CSS that targets its internal structure (e.g., hiding the last-child overflow button).

Files changed (4) +83 / -1

Bug fix (4) +83 / -1
component-overview.module.scssMake Use-package tab strip scrollable and disable overflow button on mobile +17/-0

Make Use-package tab strip scrollable and disable overflow button on mobile

• Adds a max-width ($br-md) rule that makes the use-package nav horizontally scrollable and sizes the nav to max-content so ResponsiveNavbar’s fit check passes. Hides the last child (the “…” overflow control) to prevent reserved width from hiding the final tab.

components/ui/component-meta/component-overview.module.scss

menu.module.scssMinimal-mode mobile: split component menu into two rows and keep nav tabs visible +45/-0

Minimal-mode mobile: split component menu into two rows and keep nav tabs visible

• Introduces .topBarMinimal styles (mobile-only) that switch the menu container to display: contents so its children participate in the wrapping workspace top bar. Forces the nav row to full-width with horizontal scrolling, disables ResponsiveNavbar’s overflow button, and pins first-row items to 46px height to avoid pinned-widget height inflation pushing the nav out of view.

scopes/component/component/ui/menu/menu.module.scss

menu.tsxApply topBarMinimal class when rendering ComponentMenu in minimal mode +1/-1

Apply topBarMinimal class when rendering ComponentMenu in minimal mode

• Conditionally adds the topBarMinimal CSS class to the component menu top bar when isMinimal is true, enabling the mobile wrapping/two-row behavior without affecting desktop/non-minimal layouts.

scopes/component/component/ui/menu/menu.tsx

workspace.module.scssAllow minimal workspace top bar to wrap on mobile and clip long breadcrumbs +20/-0

Allow minimal workspace top bar to wrap on mobile and clip long breadcrumbs

• Imports breakpoints and adds mobile rules to keep the breadcrumb row at 46px, clip overflow, and prevent long paths from pushing actions off-screen. Enables flex-wrap and auto height for the minimal top bar so the component menu’s nav tabs can wrap into a second row.

scopes/workspace/workspace/ui/workspace/workspace.module.scss

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Fragile overflow hiding selector 🐞 Bug ⚙ Maintainability
Description
Mobile CSS hides the overflow control using > div:last-child { display: none; }, which depends on
the internal DOM structure of ResponsiveNavbar/ContentTabs (a dependency-owned component). If
the library changes wrapper elements or ordering, this rule can hide the wrong element (e.g., a real
tab) or fail to hide the overflow control, reintroducing navigation issues.
Code

scopes/component/component/ui/menu/menu.module.scss[R43-47]

+        min-width: max-content;
+
+        > div:last-child {
+          display: none;
+        }
Evidence
The PR introduces structural last-child selectors in two different tab implementations (component
overview and component menu). The component menu explicitly uses the dependency ResponsiveNavbar,
and the component overview uses the dependency ContentTabs, so the selectors are coupled to
dependency-owned DOM and can break on dependency changes.

scopes/component/component/ui/menu/menu.module.scss[20-49]
components/ui/component-meta/component-overview.module.scss[47-62]
scopes/component/component/ui/menu/menu-nav.tsx[1-103]
components/ui/component-meta/component-overview.tsx[268-275]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Mobile styles hide the overflow UI by selecting the last direct `div` child (`> div:last-child`). This is tightly coupled to dependency-generated markup, making the fix vulnerable to library DOM changes.
### Issue Context
- `CollapsibleMenuNav` renders `ResponsiveNavbar` from `@teambit/design.navigation.responsive-navbar`.
- `ComponentOverview` renders `ContentTabs` from `@teambit/design.navigation.content-tabs`.
- The PR hides the overflow element structurally rather than via a stable class/data-attribute.
### Fix Focus Areas
- scopes/component/component/ui/menu/menu.module.scss[38-49]
- components/ui/component-meta/component-overview.module.scss[52-61]
- scopes/component/component/ui/menu/menu-nav.tsx[91-103]
- components/ui/component-meta/component-overview.tsx[268-275]
### Suggested fix
1. Prefer a stable hook to hide the overflow control:
- For `ResponsiveNavbar`, use `secondaryNavClassName` (already plumbed through `CollapsibleMenuNav`) to apply a dedicated class to the overflow element, and hide that class in CSS at mobile sizes.
2. Avoid `:last-child` structural selectors; if you must, scope them to a stable attribute/class from the rendered element (e.g., `:global([data-...])`) after confirming what the dependency renders.
3. If `ContentTabs` doesn’t expose a className/prop for its overflow button, consider adding (or requesting upstream) an explicit `moreButtonClassName`/`overflowButtonClassName` prop, so the fix remains stable across DOM changes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Grid min width overflow 🐞 Bug ≡ Correctness
Description
The blank-state grid now uses repeat(auto-fit, minmax(280px, 1fr)), which enforces a 280px minimum
track size and can create horizontal overflow when the container’s content width drops below 280px
(especially after padding). This can regress embedded/narrow layouts by forcing sideways scrolling
instead of stacking fluidly.
Code

scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[R132-135]

+  // narrow screens. 280px is the width at which a full command + Copy button still fit, so
+  // the cards sit side by side when there's room and stack when there isn't.
+  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
Evidence
The PR changes the grid to a fixed 280px minimum track size and the same stylesheet applies padding
that reduces available content width on small screens, making horizontal overflow possible when the
container becomes too narrow.

scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[20-34]
scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[126-136]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`minmax(280px, 1fr)` hard-codes a minimum column width of 280px; if the grid container becomes narrower than that (after padding), the grid can overflow horizontally.
### Issue Context
The `.body` element adds horizontal padding on small screens, reducing available content width; in narrow embeds/containers this can push the grid below 280px.
### Fix Focus Areas
- scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[20-34]
- scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[126-136]
### Suggested fix
Use a min that never exceeds the container width, e.g.:
- `grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));`
Alternatively, add a very-narrow breakpoint that switches to `1fr` below the point where padding + gaps make 280px impossible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Overbroad height override 🐞 Bug ⚙ Maintainability
Description
In minimal mobile, .topBarMinimal applies height: 46px !important to every direct child except
.leftSide, which will also affect any future direct children added to the top bar. This makes the
layout sensitive to DOM shape and makes later per-item sizing changes hard (because !important
wins over component styles/inline height).
Code

scopes/component/component/ui/menu/menu.module.scss[R55-57]

+    > :not(.leftSide) {
+      height: 46px !important;
+    }
Evidence
The PR adds a broad selector that targets all direct children, and the current DOM structure shows
pinned widgets and right-side controls are direct children in minimal mode; pinned widgets also set
inline height: '100%', explaining why a broad override was introduced but also why it is sensitive
to future structure changes.

scopes/component/component/ui/menu/menu.module.scss[51-61]
scopes/component/component/ui/menu/menu.tsx[166-180]
scopes/component/component/ui/menu/menu.tsx[404-413]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A catch-all selector (`> :not(.leftSide)`) forces `height: 46px !important` on every current and future direct child of `.topBarMinimal`, coupling layout correctness to DOM structure and making future adjustments difficult.
### Issue Context
- The current children include `.leftSide`, pinned widgets, and `.rightSide`.
- Pinned widgets explicitly set inline `height: '100%'`, which is why the PR needed an override.
### Fix Focus Areas
- scopes/component/component/ui/menu/menu.module.scss[51-61]
- scopes/component/component/ui/menu/menu.tsx[166-180]
- scopes/component/component/ui/menu/menu.tsx[404-413]
### Suggested fix
1. Make the override explicit rather than structural:
- Add a dedicated class for pinned widgets (e.g., pass `className={styles.pinnedWidget}` from `PinnedWidgetComponent`, or wrap pinned widgets in a `<div className={styles.pinnedWidgetsRow}>...</div>`).
- Apply height rules to `.rightSide` and `.pinnedWidget` specifically instead of `:not(.leftSide)`.
2. Remove `!important` if possible by increasing specificity via class targeting (so downstream components can still override when needed).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous review results

Review updated until commit cd193eb ⚖️ Balanced

Results up to commit 384c147


🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)


Remediation recommended
1. Fragile overflow hiding selector 🐞 Bug ⚙ Maintainability
Description
Mobile CSS hides the overflow control using > div:last-child { display: none; }, which depends on
the internal DOM structure of ResponsiveNavbar/ContentTabs (a dependency-owned component). If
the library changes wrapper elements or ordering, this rule can hide the wrong element (e.g., a real
tab) or fail to hide the overflow control, reintroducing navigation issues.
Code

scopes/component/component/ui/menu/menu.module.scss[R43-47]

+        min-width: max-content;
+
+        > div:last-child {
+          display: none;
+        }
Evidence
The PR introduces structural last-child selectors in two different tab implementations (component
overview and component menu). The component menu explicitly uses the dependency ResponsiveNavbar,
and the component overview uses the dependency ContentTabs, so the selectors are coupled to
dependency-owned DOM and can break on dependency changes.

scopes/component/component/ui/menu/menu.module.scss[20-49]
components/ui/component-meta/component-overview.module.scss[47-62]
scopes/component/component/ui/menu/menu-nav.tsx[1-103]
components/ui/component-meta/component-overview.tsx[268-275]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Mobile styles hide the overflow UI by selecting the last direct `div` child (`> div:last-child`). This is tightly coupled to dependency-generated markup, making the fix vulnerable to library DOM changes.

### Issue Context
- `CollapsibleMenuNav` renders `ResponsiveNavbar` from `@teambit/design.navigation.responsive-navbar`.
- `ComponentOverview` renders `ContentTabs` from `@teambit/design.navigation.content-tabs`.
- The PR hides the overflow element structurally rather than via a stable class/data-attribute.

### Fix Focus Areas
- scopes/component/component/ui/menu/menu.module.scss[38-49]
- components/ui/component-meta/component-overview.module.scss[52-61]
- scopes/component/component/ui/menu/menu-nav.tsx[91-103]
- components/ui/component-meta/component-overview.tsx[268-275]

### Suggested fix
1. Prefer a stable hook to hide the overflow control:
  - For `ResponsiveNavbar`, use `secondaryNavClassName` (already plumbed through `CollapsibleMenuNav`) to apply a dedicated class to the overflow element, and hide that class in CSS at mobile sizes.
2. Avoid `:last-child` structural selectors; if you must, scope them to a stable attribute/class from the rendered element (e.g., `:global([data-...])`) after confirming what the dependency renders.
3. If `ContentTabs` doesn’t expose a className/prop for its overflow button, consider adding (or requesting upstream) an explicit `moreButtonClassName`/`overflowButtonClassName` prop, so the fix remains stable across DOM changes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Grid min width overflow 🐞 Bug ≡ Correctness
Description
The blank-state grid now uses repeat(auto-fit, minmax(280px, 1fr)), which enforces a 280px minimum
track size and can create horizontal overflow when the container’s content width drops below 280px
(especially after padding). This can regress embedded/narrow layouts by forcing sideways scrolling
instead of stacking fluidly.
Code

scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[R132-135]

+  // narrow screens. 280px is the width at which a full command + Copy button still fit, so
+  // the cards sit side by side when there's room and stack when there isn't.
+  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
Evidence
The PR changes the grid to a fixed 280px minimum track size and the same stylesheet applies padding
that reduces available content width on small screens, making horizontal overflow possible when the
container becomes too narrow.

scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[20-34]
scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[126-136]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`minmax(280px, 1fr)` hard-codes a minimum column width of 280px; if the grid container becomes narrower than that (after padding), the grid can overflow horizontally.

### Issue Context
The `.body` element adds horizontal padding on small screens, reducing available content width; in narrow embeds/containers this can push the grid below 280px.

### Fix Focus Areas
- scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[20-34]
- scopes/workspace/workspace/ui/workspace/workspace-overview/workspace-blank-state.module.scss[126-136]

### Suggested fix
Use a min that never exceeds the container width, e.g.:
- `grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));`

Alternatively, add a very-narrow breakpoint that switches to `1fr` below the point where padding + gaps make 280px impossible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational
3. Overbroad height override 🐞 Bug ⚙ Maintainability
Description
In minimal mobile, .topBarMinimal applies height: 46px !important to every direct child except
.leftSide, which will also affect any future direct children added to the top bar. This makes the
layout sensitive to DOM shape and makes later per-item sizing changes hard (because !important
wins over component styles/inline height).
Code

scopes/component/component/ui/menu/menu.module.scss[R55-57]

+    > :not(.leftSide) {
+      height: 46px !important;
+    }
Evidence
The PR adds a broad selector that targets all direct children, and the current DOM structure shows
pinned widgets and right-side controls are direct children in minimal mode; pinned widgets also set
inline height: '100%', explaining why a broad override was introduced but also why it is sensitive
to future structure changes.

scopes/component/component/ui/menu/menu.module.scss[51-61]
scopes/component/component/ui/menu/menu.tsx[166-180]
scopes/component/component/ui/menu/menu.tsx[404-413]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A catch-all selector (`> :not(.leftSide)`) forces `height: 46px !important` on every current and future direct child of `.topBarMinimal`, coupling layout correctness to DOM structure and making future adjustments difficult.

### Issue Context
- The current children include `.leftSide`, pinned widgets, and `.rightSide`.
- Pinned widgets explicitly set inline `height: '100%'`, which is why the PR needed an override.

### Fix Focus Areas
- scopes/component/component/ui/menu/menu.module.scss[51-61]
- scopes/component/component/ui/menu/menu.tsx[166-180]
- scopes/component/component/ui/menu/menu.tsx[404-413]

### Suggested fix
1. Make the override explicit rather than structural:
  - Add a dedicated class for pinned widgets (e.g., pass `className={styles.pinnedWidget}` from `PinnedWidgetComponent`, or wrap pinned widgets in a `<div className={styles.pinnedWidgetsRow}>...</div>`).
  - Apply height rules to `.rightSide` and `.pinnedWidget` specifically instead of `:not(.leftSide)`.
2. Remove `!important` if possible by increasing specificity via class targeting (so downstream components can still override when needed).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit e4818bb


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Great, no issues found!

Qodo reviewed your code and found no material issues that require review
Qodo Logo

Comment thread scopes/component/component/ui/menu/menu.module.scss
Comment thread scopes/component/component/ui/menu/menu.module.scss
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 384c147

@luvkapur
luvkapur enabled auto-merge (squash) August 12, 2026 14:13
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit cd193eb

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.

2 participants