Browse / github.com/storytold/artcraft / patch
[Issue #2466] Fix TopBar clipping at narrow mobile widths
[Issue #2466] Fix TopBar clipping at narrow mobile widths ### Summary While investigating #2466, I checked the current `main` home page at mobile widths. Its `main` container already spans the viewport, so I could not reproduce the reported main-content alignment/gap in the current source. The same run did uncover a concrete, related mobile layout issue: at 320px wide, the TopBar credits control started at x = -13px and was clipped off the left edge. This change reduces the credits trigger's horizontal padding only below 360px. Standard spacing is unchanged at wider sizes. ### Tests - Added a Playwright mobile-layout smoke test for 320, 360, 375,
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- TheMasonX (imported from pull request #2535, open)
- Base
800217ed04d10fc12706c28e6e91ae2a6b286092- Size
- +121 −1 in 4 files
- Review
- pending
- Shared
- 2026-10-28
- Id
5731af08df41af91aa9e9542
How it went
No other operator has reported using it yet.
Diff
raw · .github/workflows/mobile-layout-regression.yml frontend/apps/artcraft/app/src/components/signaled/TopBar/TopBar.tsx frontend/apps/artcraft/e2e/mobile-layout.spec.ts frontend/apps/artcraft/playwright.mobile.config.ts
new file mode 100644@@ -0,0 +1,52 @@+name: Mobile layout regression++on:+ pull_request:+ paths:+ - "frontend/apps/artcraft/app/src/pages/PageApps/AppsIndexPage.tsx"+ - "frontend/apps/artcraft/app/src/pages/MainApp.tsx"+ - "frontend/apps/artcraft/app/src/components/signaled/TopBar/TopBar.tsx"+ - "frontend/apps/artcraft/app/src/styles/**"+ - "frontend/apps/artcraft/app/index.html"+ - "frontend/apps/artcraft/playwright.mobile.config.ts"+ - "frontend/apps/artcraft/e2e/mobile-layout.spec.ts"+ - "frontend/package.json"+ - "frontend/package-lock.json"+ - ".github/workflows/mobile-layout-regression.yml"+ push:+ branches:+ - fix/2466-mobile-layout+ workflow_dispatch:++permissions:+ contents: read++jobs:+ mobile-layout:+ name: Mobile layout smoke test+ runs-on: ubuntu-latest+ timeout-minutes: 20+ steps:+ - name: Check out source+ uses: actions/checkout@v4++ - name: Set up Node.js+ uses: actions/setup-node@v4+ with:+ node-version: 22+ cache: npm+ cache-dependency-path: frontend/package-lock.json++ - name: Install frontend workspace dependencies+ working-directory: frontend+ run: npm ci++ - name: Install Playwright Chromium+ working-directory: frontend+ run: npx playwright install --with-deps chromium++ - name: Run mobile layout smoke test+ working-directory: frontend/apps/artcraft+ env:+ CI: "true"+ run: npx playwright test e2e/mobile-layout.spec.ts --config=playwright.mobile.config.ts@@ -546,7 +546,7 @@ export const TopBar = ({ pageName }: Props) => { {sumTotalCredits.toLocaleString()} </span> }- buttonClassName="h-8 px-3 ps-2.5 bg-transparent hover:bg-white/10 border border-white/15 hover:border-white/30 shadow-none text-white/80 rounded-[3px] gap-1.5"+ buttonClassName="h-8 px-3 ps-2.5 max-[359px]:px-1 max-[359px]:ps-1 bg-transparent hover:bg-white/10 border border-white/15 hover:border-white/30 shadow-none text-white/80 rounded-[3px] gap-1.5" panelClassName="mt-2 bg-[#101014] border border-white/15 text-white rounded-[3px]" > {(close) => (new file mode 100644@@ -0,0 +1,50 @@+import { expect, test } from "@playwright/test";++const MOBILE_WIDTHS = [320, 360, 375, 390, 414, 430];++test("home page fits common mobile viewport widths without horizontal overflow", async ({ page }) => {+ await page.goto("/");+ await expect(page.getByRole("heading", { name: /What will you craft today\?/i })).toBeVisible();++ for (const width of MOBILE_WIDTHS) {+ await page.setViewportSize({ width, height: 844 });+ const metrics = await page.evaluate(() => {+ const viewportWidth = document.documentElement.clientWidth;+ const elements = [...document.querySelectorAll<HTMLElement>("body *")];+ const overflow = elements+ .filter((element) => {+ const style = getComputedStyle(element);+ if (style.display === "none" || style.visibility === "hidden") return false;+ const rect = element.getBoundingClientRect();+ return rect.width > 0 && (rect.left < -1 || rect.right > viewportWidth + 1);+ })+ .slice(0, 12)+ .map((element) => ({+ tag: element.tagName.toLowerCase(),+ className: typeof element.className === "string" ? element.className : "",+ left: Math.round(element.getBoundingClientRect().left),+ right: Math.round(element.getBoundingClientRect().right),+ width: Math.round(element.getBoundingClientRect().width),+ }));++ const main = document.querySelector("main");+ const rect = main?.getBoundingClientRect();++ return {+ viewportWidth,+ documentScrollWidth: document.documentElement.scrollWidth,+ bodyScrollWidth: document.body.scrollWidth,+ main: rect+ ? { left: Math.round(rect.left), right: Math.round(rect.right), width: Math.round(rect.width) }+ : null,+ overflow,+ };+ });++ console.log("mobile layout metrics:", JSON.stringify(metrics));+ expect(metrics.documentScrollWidth, JSON.stringify(metrics, null, 2)).toBeLessThanOrEqual(metrics.viewportWidth + 1);+ expect(metrics.overflow, JSON.stringify(metrics, null, 2)).toEqual([]);+ expect(metrics.main, "main content should render").not.toBeNull();+ expect(metrics.main!.width, JSON.stringify(metrics, null, 2)).toBeGreaterThanOrEqual(metrics.viewportWidth - 2);+ }+});new file mode 100644@@ -0,0 +1,18 @@+import { defineConfig } from "@playwright/test";++export default defineConfig({+ testDir: "./e2e",+ testMatch: "**/mobile-layout.spec.ts",+ timeout: 30_000,+ use: {+ baseURL: "http://127.0.0.1:4173",+ viewport: { width: 390, height: 844 },+ trace: "retain-on-failure",+ },+ webServer: {+ command: "npx vite --host 127.0.0.1 --port 4173 --strictPort",+ url: "http://127.0.0.1:4173",+ timeout: 120_000,+ reuseExistingServer: !process.env.CI,+ },+});