Skip to content

feat: enhance mobile responsiveness and scrollbar visibility across components#120

Merged
JoachimLK merged 3 commits intomainfrom
feat/mobile-frendlty
Mar 23, 2026
Merged

feat: enhance mobile responsiveness and scrollbar visibility across components#120
JoachimLK merged 3 commits intomainfrom
feat/mobile-frendlty

Conversation

@JoachimLK
Copy link
Copy Markdown
Contributor

@JoachimLK JoachimLK commented Mar 22, 2026

Summary

  • What does this PR change?
  • Why is this needed?

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Chore

Validation

  • I tested locally
  • I added/updated relevant documentation
  • I verified multi-tenant scoping and auth behavior for affected API paths

DCO

  • All commits in this PR are signed off (Signed-off-by) via git commit -s

Summary by CodeRabbit

  • New Features

    • Mobile settings navigation and a dedicated settings layout for improved mobile workflows.
    • Mobile candidate bottom bar with horizontal scrolling and position indicator.
  • Improvements

    • New thin and hidden scrollbar utilities for cleaner, cross‑browser scrolling.
    • Widespread responsive refinements: tighter spacing, adaptive typography, horizontal nav/table scrolling, and layout/scroll region adjustments.
    • Added viewport meta for better mobile display.

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-120 March 22, 2026 14:20 Destroyed
@railway-app
Copy link
Copy Markdown

railway-app Bot commented Mar 22, 2026

🚅 Deployed to the reqcore-pr-120 environment in applirank

Service Status Web Updated (UTC)
applirank ✅ Success (View Logs) Web Mar 23, 2026 at 11:13 am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d09f618-2c82-4f31-b9e0-76119f8af402

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb9723 and d58db54.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • package.json
✅ Files skipped from review due to trivial changes (1)
  • package.json

📝 Walkthrough

Walkthrough

Responsive and mobile-first UI updates: added two scrollbar utility classes and viewport meta; many components/pages refactored for responsive padding, horizontal scrolling, hidden thin/hidden scrollbars, a new settings layout and mobile settings nav, and a mobile candidate bottom bar with an auto-scroll watcher.

Changes

Cohort / File(s) Summary
CSS utilities & config
app/assets/css/main.css, nuxt.config.ts
Added .scrollbar-none and .scrollbar-thin utilities; added global viewport <meta> tag.
Global layouts
app/layouts/dashboard.vue, app/layouts/settings.vue
Dashboard constrained to h-screen with a scrollable main; added new settings layout that initializes session/org state and controls demo/upsell UI.
Settings navigation
app/components/SettingsMobileNav.vue, app/components/SettingsSidebar.vue
New mobile horizontal settings nav with active-link detection and hidden scrollbar; sidebar background/overscroll styling adjusted.
Topbar & banners
app/components/AppTopBar.vue, app/components/DemoUpsellBanner.vue
Made sub-navigation horizontally scrollable with nowrap/shrink guards; adjusted demo banner positioning/responsiveness.
Candidate / jobs pages (mobile)
app/pages/dashboard/jobs/[id]/index.vue, app/components/CandidateDetailSidebar.vue, app/pages/dashboard/jobs/[id]/candidates.vue
Added showMobileDetail flag, mobile-only bottom candidate bar (mobileBottomBar) with auto-scroll watcher; converted sidebars/lists to responsive widths; enabled horizontal scrolling with hidden scrollbars; hid less-critical table columns on small screens.
Modals & tabs
app/components/InterviewEmailModal.vue, app/components/CandidateDetailSidebar.vue
Reduced horizontal padding to px-4 sm:px-6; enabled horizontal tab scrolling, added scrollbar-none, whitespace-nowrap, and shrink-0 to tab buttons.
Settings pages
app/pages/dashboard/settings/*.vue (account.vue, ai.vue, index.vue, integrations.vue, members.vue), app/pages/dashboard/settings.vue
Standardized responsive padding (px-4 sm:px-6), added shrink-0 to icon/avatar wrappers, converted rows to flex-col sm:flex-row; switched page to use new settings layout.
Dashboard & minor pages
app/pages/dashboard/index.vue, app/pages/dashboard/updates.vue
Adjusted header spacing, responsive typography, stat grid gaps, and version details grid for better small-screen stacking.
Package overrides
package.json
Relaxed overrides.fast-xml-parser from 5.5.6 to >=5.5.7.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant SettingsLayout
  participant AuthClient
  participant PreviewUpsell
  participant DemoBanner

  Browser->>SettingsLayout: render settings layout
  SettingsLayout->>AuthClient: authClient.useSession(useFetch)
  AuthClient-->>SettingsLayout: session data
  SettingsLayout->>SettingsLayout: compute isDemo & isDemoAccount
  alt isUpsellOpen
    SettingsLayout->>PreviewUpsell: show PreviewUpsellModal
    PreviewUpsell-->>Browser: render modal
  end
  alt isDemoAccount
    SettingsLayout->>DemoBanner: render DemoUpsellBanner (ClientOnly)
    DemoBanner-->>Browser: render banner
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hid the bars and made them slim,

Scrollers whisper, edges trim.
Navs that slide and banners bloom,
Mobile hops make room, room, room.
Hop—small screens find their vim!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description contains only placeholder template sections with no actual content filled in. The summary section lacks details about what changed and why, checkboxes remain unchecked, and DCO is not signed off. Fill in the summary section with what changed and why, select the appropriate type of change (appears to be a Feature), check relevant validation items, and ensure all commits are signed off with DCO.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: adding mobile responsiveness improvements and new scrollbar styling utilities across multiple components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mobile-frendlty

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/assets/css/main.css (1)

338-345: Avoid globally hiding scrollbars on all pointer types.

This works technically, but permanently hiding scrollbars can hurt discoverability for desktop users. Consider scoping to touch/coarse pointers.

♻️ Suggested adjustment
 .scrollbar-none {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
+  -ms-overflow-style: auto;
+  scrollbar-width: auto;
 }
 .scrollbar-none::-webkit-scrollbar {
-  display: none;
+  display: block;
 }
+
+@media (pointer: coarse) {
+  .scrollbar-none {
+    -ms-overflow-style: none;
+    scrollbar-width: none;
+  }
+  .scrollbar-none::-webkit-scrollbar {
+    display: none;
+  }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/assets/css/main.css` around lines 338 - 345, The .scrollbar-none utility
hides scrollbars for all pointer types which reduces discoverability on
desktops; update the rule so it only disables scrollbars for coarse/touch
pointers (e.g., wrap the existing .scrollbar-none rules in a media query
targeting pointer: coarse or touch) and keep default behavior for fine
pointers—locate the .scrollbar-none selector and its ::-webkit-scrollbar block
and move them inside the pointer: coarse media query so desktop (fine pointer)
users still see scrollbars.
app/components/CandidateDetailSidebar.vue (1)

389-427: Lock tab items to no-wrap for predictable horizontal scrolling.

Since the tab row now scrolls horizontally, keeping each tab non-wrapping/non-shrinking will avoid compressed or multi-line tab labels.

♻️ Suggested class tweak
- class="cursor-pointer px-3 py-2.5 text-sm font-medium transition-colors border-b-2 -mb-px"
+ class="cursor-pointer px-3 py-2.5 text-sm font-medium transition-colors border-b-2 -mb-px whitespace-nowrap shrink-0"

Apply the same addition to each tab button in this group.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/CandidateDetailSidebar.vue` around lines 389 - 427, The tab
buttons in the CandidateDetailSidebar.vue tab row (the buttons that set
activeTab to 'overview', 'documents', 'responses', and 'ai_analysis') need to be
locked to a single line and prevented from shrinking so horizontal scrolling
stays predictable; update each tab button's class list to include utility
classes to prevent wrapping and shrinking (e.g., add whitespace-nowrap and
flex-shrink-0 or equivalent) on the buttons that render "Overview", "Documents
({{ documents.length }})", the conditional "Responses ({{ responsesCount }})",
and the "AI Analysis" button so labels never wrap or compress during horizontal
scroll.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/AppTopBar.vue`:
- Around line 433-440: The mobile tabs become icon-only and lose accessible
names because the visible label (tab.label) is hidden on small screens; update
the NuxtLink for the tab (the element using isActiveRoute and rendering
<component :is="tab.icon" /> and <span>{{ tab.label }}</span>) to include an
explicit accessible name such as :aria-label="tab.label" (and optionally
:title="tab.label") so the icon-only links retain screen-reader text and
hover/tooltips when the span is hidden.

---

Nitpick comments:
In `@app/assets/css/main.css`:
- Around line 338-345: The .scrollbar-none utility hides scrollbars for all
pointer types which reduces discoverability on desktops; update the rule so it
only disables scrollbars for coarse/touch pointers (e.g., wrap the existing
.scrollbar-none rules in a media query targeting pointer: coarse or touch) and
keep default behavior for fine pointers—locate the .scrollbar-none selector and
its ::-webkit-scrollbar block and move them inside the pointer: coarse media
query so desktop (fine pointer) users still see scrollbars.

In `@app/components/CandidateDetailSidebar.vue`:
- Around line 389-427: The tab buttons in the CandidateDetailSidebar.vue tab row
(the buttons that set activeTab to 'overview', 'documents', 'responses', and
'ai_analysis') need to be locked to a single line and prevented from shrinking
so horizontal scrolling stays predictable; update each tab button's class list
to include utility classes to prevent wrapping and shrinking (e.g., add
whitespace-nowrap and flex-shrink-0 or equivalent) on the buttons that render
"Overview", "Documents ({{ documents.length }})", the conditional "Responses ({{
responsesCount }})", and the "AI Analysis" button so labels never wrap or
compress during horizontal scroll.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd8b5196-e5b8-4afd-a316-fc87df774033

📥 Commits

Reviewing files that changed from the base of the PR and between 7720fc8 and 5ecc098.

📒 Files selected for processing (11)
  • app/assets/css/main.css
  • app/components/AppTopBar.vue
  • app/components/CandidateDetailSidebar.vue
  • app/components/DemoUpsellBanner.vue
  • app/components/InterviewEmailModal.vue
  • app/pages/dashboard/index.vue
  • app/pages/dashboard/jobs/[id]/candidates.vue
  • app/pages/dashboard/jobs/[id]/index.vue
  • app/pages/dashboard/settings/members.vue
  • app/pages/dashboard/updates.vue
  • nuxt.config.ts

Comment on lines +433 to 440
class="flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium transition-all duration-200 no-underline whitespace-nowrap shrink-0"
:class="isActiveRoute(tab.to, tab.exact)
? 'bg-white dark:bg-surface-800 text-surface-900 dark:text-surface-100 shadow-sm'
: 'text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200 hover:bg-white/60 dark:hover:bg-surface-800/60'"
>
<component :is="tab.icon" class="size-3.5" />
{{ tab.label }}
<span class="hidden sm:inline">{{ tab.label }}</span>
</NuxtLink>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mobile job tabs lose accessible names when labels are hidden.

On small screens, Line 439 hides the only visible text label. These links become icon-only without an explicit accessible name.

🛠️ Suggested fix
 <NuxtLink
   v-for="tab in jobTabs"
   :key="tab.to"
   :to="$localePath(tab.to)"
+  :aria-label="tab.label"
   class="flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium transition-all duration-200 no-underline whitespace-nowrap shrink-0"
   :class="isActiveRoute(tab.to, tab.exact)
     ? 'bg-white dark:bg-surface-800 text-surface-900 dark:text-surface-100 shadow-sm'
     : 'text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200 hover:bg-white/60 dark:hover:bg-surface-800/60'"
 >
   <component :is="tab.icon" class="size-3.5" />
-  <span class="hidden sm:inline">{{ tab.label }}</span>
+  <span class="sr-only sm:not-sr-only sm:inline">{{ tab.label }}</span>
 </NuxtLink>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/AppTopBar.vue` around lines 433 - 440, The mobile tabs become
icon-only and lose accessible names because the visible label (tab.label) is
hidden on small screens; update the NuxtLink for the tab (the element using
isActiveRoute and rendering <component :is="tab.icon" /> and <span>{{ tab.label
}}</span>) to include an explicit accessible name such as
:aria-label="tab.label" (and optionally :title="tab.label") so the icon-only
links retain screen-reader text and hover/tooltips when the span is hidden.

- Adjusted padding and margin for various sections in ai.vue, index.vue, integrations.vue, members.vue to improve responsiveness and consistency.
- Introduced SettingsMobileNav component for mobile navigation in settings.
- Created settings layout to manage desktop and mobile views, including demo mode banner and upsell modal.
- Enhanced member and invite link sections for better alignment and spacing.
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-120 March 23, 2026 10:58 Destroyed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/layouts/settings.vue (1)

5-16: Consider extracting duplicated demo/auth layout state.

Lines 5-16 duplicate session/org/demo flag initialization that already exists in app/layouts/dashboard.vue (same calls + demo email check). A shared composable would reduce drift and simplify future policy changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/layouts/settings.vue` around lines 5 - 16, Extract the duplicated
session/org/demo initialization into a new composable (e.g., useDemoAuthState)
that calls authClient.useSession(useFetch), useRuntimeConfig(), useCurrentOrg(),
and usePreviewReadOnly(), and computes isDemo and isDemoAccount; then replace
the duplicated blocks in settings.vue and dashboard.vue with a single call to
the new composable (useDemoAuthState()) and use its returned refs (session,
activeOrg, isUpsellOpen, closeUpsell, isDemo, isDemoAccount).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/SettingsMobileNav.vue`:
- Around line 9-40: The settings nav arrays are duplicated (settingsNav in
SettingsMobileNav.vue vs the array in SettingsSidebar.vue) causing label drift
(e.g., "AI" vs "AI Configuration"); extract a single shared export (e.g., export
const settingsNav = [...]) into a new module and update both
SettingsMobileNav.vue and SettingsSidebar.vue to import and use that shared
settingsNav instead of defining their own arrays, ensuring the shared object
contains the label, to, icon, and exact fields (and export/import any icon
references used) so both components render identical labels and routes.

---

Nitpick comments:
In `@app/layouts/settings.vue`:
- Around line 5-16: Extract the duplicated session/org/demo initialization into
a new composable (e.g., useDemoAuthState) that calls
authClient.useSession(useFetch), useRuntimeConfig(), useCurrentOrg(), and
usePreviewReadOnly(), and computes isDemo and isDemoAccount; then replace the
duplicated blocks in settings.vue and dashboard.vue with a single call to the
new composable (useDemoAuthState()) and use its returned refs (session,
activeOrg, isUpsellOpen, closeUpsell, isDemo, isDemoAccount).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e6abec6-0aaf-464a-ba1f-d806f31cca84

📥 Commits

Reviewing files that changed from the base of the PR and between 5ecc098 and 2cb9723.

📒 Files selected for processing (12)
  • app/assets/css/main.css
  • app/components/SettingsMobileNav.vue
  • app/components/SettingsSidebar.vue
  • app/layouts/dashboard.vue
  • app/layouts/settings.vue
  • app/pages/dashboard/jobs/[id]/index.vue
  • app/pages/dashboard/settings.vue
  • app/pages/dashboard/settings/account.vue
  • app/pages/dashboard/settings/ai.vue
  • app/pages/dashboard/settings/index.vue
  • app/pages/dashboard/settings/integrations.vue
  • app/pages/dashboard/settings/members.vue
✅ Files skipped from review due to trivial changes (5)
  • app/pages/dashboard/settings/integrations.vue
  • app/pages/dashboard/settings/account.vue
  • app/pages/dashboard/settings/index.vue
  • app/pages/dashboard/settings/ai.vue
  • app/pages/dashboard/settings/members.vue
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/assets/css/main.css
  • app/pages/dashboard/jobs/[id]/index.vue

Comment on lines +9 to +40
const settingsNav = [
{
label: 'General',
to: '/dashboard/settings',
icon: Building2,
exact: true,
},
{
label: 'Members',
to: '/dashboard/settings/members',
icon: Users,
exact: true,
},
{
label: 'Integrations',
to: '/dashboard/settings/integrations',
icon: Plug,
exact: true,
},
{
label: 'AI',
to: '/dashboard/settings/ai',
icon: Brain,
exact: true,
},
{
label: 'Account',
to: '/dashboard/settings/account',
icon: UserCircle,
exact: true,
},
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Desktop/mobile settings labels are drifting.

Line 29 uses "AI", while desktop sidebar uses "AI Configuration" in app/components/SettingsSidebar.vue Line 32. Since both components maintain separate nav arrays, this will keep drifting. Consider extracting a shared settingsNav source and reusing it in both components.

Quick consistency patch
-    label: 'AI',
+    label: 'AI Configuration',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const settingsNav = [
{
label: 'General',
to: '/dashboard/settings',
icon: Building2,
exact: true,
},
{
label: 'Members',
to: '/dashboard/settings/members',
icon: Users,
exact: true,
},
{
label: 'Integrations',
to: '/dashboard/settings/integrations',
icon: Plug,
exact: true,
},
{
label: 'AI',
to: '/dashboard/settings/ai',
icon: Brain,
exact: true,
},
{
label: 'Account',
to: '/dashboard/settings/account',
icon: UserCircle,
exact: true,
},
]
const settingsNav = [
{
label: 'General',
to: '/dashboard/settings',
icon: Building2,
exact: true,
},
{
label: 'Members',
to: '/dashboard/settings/members',
icon: Users,
exact: true,
},
{
label: 'Integrations',
to: '/dashboard/settings/integrations',
icon: Plug,
exact: true,
},
{
label: 'AI Configuration',
to: '/dashboard/settings/ai',
icon: Brain,
exact: true,
},
{
label: 'Account',
to: '/dashboard/settings/account',
icon: UserCircle,
exact: true,
},
]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/SettingsMobileNav.vue` around lines 9 - 40, The settings nav
arrays are duplicated (settingsNav in SettingsMobileNav.vue vs the array in
SettingsSidebar.vue) causing label drift (e.g., "AI" vs "AI Configuration");
extract a single shared export (e.g., export const settingsNav = [...]) into a
new module and update both SettingsMobileNav.vue and SettingsSidebar.vue to
import and use that shared settingsNav instead of defining their own arrays,
ensuring the shared object contains the label, to, icon, and exact fields (and
export/import any icon references used) so both components render identical
labels and routes.

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-120 March 23, 2026 11:08 Destroyed
@JoachimLK JoachimLK merged commit ab939bb into main Mar 23, 2026
5 checks passed
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