Skip to content

FE-19: mobile-responsive board, toolbar, and dialog#944

Merged
Chris0Jeky merged 9 commits intomainfrom
feat/fe-19-mobile-responsive
Apr 23, 2026
Merged

FE-19: mobile-responsive board, toolbar, and dialog#944
Chris0Jeky merged 9 commits intomainfrom
feat/fe-19-mobile-responsive

Conversation

@Chris0Jeky
Copy link
Copy Markdown
Owner

Closes #860 (partially — see Deferred below).

Summary

  • Board view now stacks columns vertically on ≤640px viewports, removing the fixed 16rem column width and horizontal-only scroll that made the kanban unusable on phones.
  • TdDialog (shared primitive powering CardModal, BoardSettingsModal, LabelManager, StarterPackCatalog, KeyboardHelp) becomes full-bleed at ≤640px with 100dvh height and stacked footer actions.
  • Board toolbar + action rail enforce 44×44px tap targets on mobile; primary actions span the row.
  • New @mobile Playwright test asserts vertical-stack layout and zero horizontal document overflow on a two-column board.

Surfaces covered

  • components/board/BoardCanvas.vue — vertical lanes, page-level scroll on mobile
  • components/board/ColumnLane.vue — full-width lanes, 16px input font to avoid iOS zoom, 44px buttons
  • components/board/BoardToolbar.vue — wrap actions, 44px tap targets, full-row primary
  • components/board/BoardActionRail.vue — 2-up wrap grid, 44px buttons, full-width primary
  • components/ui/TdDialog.vue — full-screen dialog on mobile (cascades to all TdDialog consumers)

Already in place (no change needed)

  • ShellSidebar + AppShell already ship a hamburger/off-canvas drawer with 44px nav items.
  • CaptureModal already had dedicated mobile styling.
  • Tailwind default breakpoints (sm:640 / md:768 / lg:1024) already match the acceptance criteria.

Deferred (follow-up)

The issue is sized at L (16h); this PR focuses on the highest-leverage board + dialog fixes. Out of scope for this slice:

  • Mobile pass on secondary views (Review, AutomationChat, Calendar, Metrics, Ops). Most already have some responsive work; a dedicated follow-up should sweep the remaining scatter.
  • Tablet (640–1024px) refinements — the kanban still renders horizontally at 768px which is usable but not optimized.
  • Touch-optimized drag-and-drop between stacked columns (the existing "Move to…" menu in CardItem is still the recommended path on mobile).

Recommend filing a follow-up: "FE-19 follow-up: tablet + secondary-view mobile polish".

Test plan

  • npm run typecheck — clean
  • npm run lint — 6 pre-existing warnings, 0 new (limit 20)
  • npx vitest --run — 2607 passed
  • @mobile Playwright project (Pixel 7 + iPhone 14) — run in CI / nightly
  • Manual verification on iPhone SE (375×667) via devtools responsive mode

Adversarial notes

  • Used 100dvh (dynamic viewport units) in TdDialog so footer doesn't hide under collapsing iOS Safari chrome; paired with 100vh fallback.
  • All mobile CSS scoped under @media (max-width: 640px) so desktop ≥641px is untouched.
  • No inline style= bindings added (respecting SEC-29: Migrate CSP from unsafe-inline styles to external CSS #855).

The fixed 16rem column width combined with horizontal-only scroll made
the board view unusable below ~640px. Switch to a vertical stack with
full-width lanes at ≤640px, relax the nested card-list max-height so the
page scroller owns the scroll, and bump tap targets / input font-size to
meet iOS guidelines. Part of FE-19 (#860).
TdDialog is the shared dialog primitive (CardModal, BoardSettingsModal,
LabelManager, StarterPackCatalog, KeyboardHelp). On mobile a centered
dialog with a 560px max-width left controls crowded and occasionally
pushed footer actions off-screen when the iOS URL bar collapsed. Adopt
a full-bleed layout at ≤640px with 100dvh height, stacked footer with
44px tap targets, and no rounding. Part of FE-19 (#860).
…bile

Stack the toolbar vertically, wrap action buttons, and ensure every
icon / text / primary button is at least 44×44px so touch interactions
are reliable at iPhone SE widths. The primary Add Column / Add Card
actions take the full row so they remain discoverable. Part of FE-19
(#860).
…flow

Add a @mobile Playwright test that creates two columns, then asserts
the second column is rendered below the first (vertical stack) and
that neither the lanes nor the document itself exceed the viewport
width. Part of FE-19 (#860).
Setting min-height alone on the back button left it ~24px wide because
it wraps a raw SVG with no padding. Force inline-flex centering with
a 44px min-width so the tap target is a full square, matching the
touch-guideline minimum. Follow-up to self-review of FE-19 (#860).
@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial self-review

Findings (addressed in a51fa6e):

  • BoardToolbar back-btn had min-height: 44px but no width constraint, so it rendered ~24px wide (SVG-only button with no padding). Added min-width: 44px + inline-flex centering so the tap target is a full 44×44 square.

Findings (no action needed, rationale below):

  • Dialog transform animation — The enter/leave transform: scale(0.95) translateY(8px) still applies on mobile, but with 100% width + 100dvh height the transform is barely perceptible. Keeping it for consistency; no layout jank observed.
  • Column drag-handle button on mobile — Still visible and tappable but touch-based column DnD isn't supported. The primary mobile path for moving cards is the Move to… menu on CardItem, which works at 44px tap size. Not worth hiding the handle since desktop drag users may still resize to tablet.
  • Existing @mobile card-edit test (modal width > 200) — CardModal now flexes to ~100% viewport width on mobile, so at 390px (iPhone 14) this is still satisfied.
  • Existing @mobile bodyBox.width ≤ viewportSize+20 — Still valid; vertical stack strictly reduces overflow risk.

Desktop regression risk: All new styles are scoped under @media (max-width: 640px). No desktop (≥641px) path touched. Typecheck clean; vitest 2607/2607 passing.

SignalR / realtime: The board fetches + SignalR subscribe in onMounted; vertical stack doesn't change DOM identity of columns/cards (same [data-column-id] / [data-card-id] selectors), so realtime patches and presence rendering are unaffected.

CLS / dvh gotcha: TdDialog uses 100dvh with a 100vh fallback above it — when iOS Safari's URL bar collapses, the dialog height settles to the final dvh value instead of re-laying out mid-interaction. Footer actions sit at the bottom with flex-direction: column-reverse, so they stay above the browser chrome even at the worst collapse moment.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces mobile responsiveness for the board view, including vertical stacking of lanes, full-screen dialogs, and accessible 44px tap targets. It also adds an E2E test to verify the vertical stacking behavior on small screens. Feedback includes ensuring the back button has a consistent minimum width for accessibility and using design tokens instead of hardcoded pixel values for font sizes to maintain consistency with the design system.

Comment on lines +372 to +374
.td-board-toolbar__icon-btn,
.td-board-toolbar__back-btn {
min-width: 44px;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The back-btn is essentially an icon button and should have a min-width of 44px to ensure a square and accessible tap target, consistent with the other icon buttons in the toolbar. Currently, it only has min-height set, which may result in a narrow hit area.

  .td-board-toolbar__icon-btn,
  .td-board-toolbar__back-btn {
    min-width: 44px;
  }


.td-column-lane__card-input {
/* Prevent iOS zoom on focus: font-size >= 16px. */
font-size: 16px;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Use the design token --td-font-lg instead of hardcoding 16px. This maintains consistency with the project's design system while still fulfilling the requirement to prevent iOS browser zoom on input focus (as --td-font-lg is defined as 1rem/16px).

    font-size: var(--td-font-lg);

Replace hardcoded 16px with the design token --td-font-lg (1rem / 16px)
on the mobile .td-column-lane__card-input rule. Keeps iOS zoom-on-focus
prevention while keeping typography under a single token.
- Add padding with env(safe-area-inset-*) so iOS notch/home-indicator
  does not cover dialog header or footer actions on full-bleed layout.
- Declare max-height/height: 100vh before 100dvh so iOS Safari <= 15.4
  (no dvh support) still gets full-viewport height instead of collapsing
  to content-sized dialog.
@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Round 2 adversarial review

Bot findings triaged

  1. frontend/taskdeck-web/src/components/board/BoardToolbar.vue:374 (gemini-code-assist, medium) — Already addressed. The comment was posted against commit a51fa6e7 but the very same commit already introduced .td-board-toolbar__icon-btn, .td-board-toolbar__back-btn { min-width: 44px; ... } (see lines 372-378). The bot appears to have flagged the before-state in its diff hunk and did not re-evaluate the after-state. No action needed.

  2. frontend/taskdeck-web/src/components/board/ColumnLane.vue:586 (gemini-code-assist, medium) — Valid, fixed in e7f281f6. Replaced hardcoded font-size: 16px with var(--td-font-lg) (design-tokens.css defines --td-font-lg: 1rem = 16px), preserving iOS zoom-on-focus prevention while staying consistent with the project's token system.

Own new findings (round 1 did not raise these)

  1. TdDialog has no vh fallback for height: 100dvh — iOS Safari ≤ 15.4 doesn't support dvh, and while the existing max-height block correctly declares 100vh before 100dvh, height was only written as 100dvh. Older Safari would fall back to auto/content height, which combined with align-items: stretch on the backdrop is mostly salvageable but non-deterministic. Fixed in f90ebace: added height: 100vh before height: 100dvh.

  2. Full-bleed dialog ignores iOS safe-area insets — On iPhone with notch/home indicator, a full-viewport dialog with padding: var(--td-space-4) could put the footer actions under the home bar or the title under the notch. Fixed in f90ebace: padding now uses max(var(--td-space-4), env(safe-area-inset-*)) on all four sides.

  3. Tailwind arbitrary values / token duplication — Grepped the diff: no new arbitrary Tailwind values (min-h-[...], w-[...], etc.) were introduced. All new rules use existing CSS custom properties (--td-space-*, --td-font-*) except the 44px tap-target minimum, which is an a11y spec constant (WCAG 2.5.5), not a token candidate.

  4. Desktop column DnD with vertical stackBoardCanvas.vue flips flex-direction only inside @media (max-width: 640px); desktop DnD layout is untouched. HTML5 @dragstart/@dragover/@drop handlers are orthogonal to flex direction and continue to receive the same events on desktop. Touch DnD remains out of scope (explicitly deferred).

  5. @mobile Playwright flake risk — The new test uses Date.now() for unique board/column names, .first() to avoid strict-mode collisions, and a ±2/±4px tolerance on bounding-box math. The mobile project inherits the shared timeout: 45_000 and expect.timeout: 8_000. No obvious flake surface beyond what desktop already has.

  6. No new inline styles — Grepped the diff for :style / style= — zero additions. No collision with SEC-29 (SEC-29: Migrate CSP from unsafe-inline styles to external CSS #855/sec: tighten style-src CSP (SEC-29, partial) #945) direction.

  7. Accessibility — No new interactive elements, no hamburger/nav toggle added, no focus-order changes. aria-* attributes on existing elements are untouched. No a11y regression.

  8. SignalR reconnection on mobile layout change — Media queries are pure CSS; no JS-level remount is triggered when crossing the 640px threshold. SignalR hub lifecycle is bound to the BoardView component, not the canvas layout. No risk.

CI

CI was 15/15 green at the start of round 2. After my two new commits (e7f281f6, f90ebace), CI is currently running against f90ebace. The changes are CSS-only inside <style scoped> blocks; frontend typecheck, lint (no new errors, 6 pre-existing warnings in unrelated views), and vitest (2607/2607 passing) all succeeded locally.

Verdict

Ready to merge pending CI re-run. All bot findings addressed, two additional iOS-polish fixes applied. No pre-existing CI failures observed. Deferred items (tablet polish, secondary-view mobile sweep, touch DnD) remain correctly out of scope.

Switch .td-dialog__footer from column-reverse to column on mobile so
keyboard focus traverses buttons in the same order they appear on
screen (WCAG 2.4.3 Focus Order). With column-reverse the primary
action would render on top but focus still started on the secondary
action below, creating a confusing mismatch for keyboard and
assistive-tech users.
@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Round 2 adversarial review (fresh pass)

Independent second look — overlaps with earlier round-2 comment are noted so this adds signal, not noise.

Bot findings triaged

  1. frontend/taskdeck-web/src/components/board/BoardToolbar.vue:374 (gemini-code-assist, medium — back-btn min-width: 44px) — Resolved in a51fa6e7 (already merged into PR head). Verified: .td-board-toolbar__icon-btn, .td-board-toolbar__back-btn { min-width: 44px; ... } sits in the scoped style block, with display: inline-flex; align-items: center; justify-content: center; centering the SVG inside the 44×44 surface. No action needed.

  2. frontend/taskdeck-web/src/components/board/ColumnLane.vue:586 (gemini-code-assist, medium — hardcoded 16px) — Resolved in e7f281f6. .td-column-lane__card-input now uses font-size: var(--td-font-lg). Verified --td-font-lg resolves to 1rem (16px) so the iOS-zoom invariant is preserved while staying on-token.

Own new findings

  1. Mobile dialog footer had mismatched visual vs. tab order (WCAG 2.4.3)Fixed in d91eb5ab (new commit pushed this session). TdDialog uses flex-direction: column-reverse on the mobile footer. Call sites (e.g. CardModal.vue delete-confirm) emit slots in DOM order [Cancel, Delete]. column-reverse only reverses visual order, not tab/focus order, so keyboard users see Delete on top but focus still lands on Cancel first. Switched to flex-direction: column so visual order and DOM/tab order stay aligned. The primary action renders at the top on mobile — less iOS-native than "primary at thumb-reach," but WCAG Focus Order is non-negotiable. All 2607 vitest tests pass; typecheck clean; no new lint warnings (6 pre-existing unrelated warnings remain).

  2. Safe-area insets rely on viewport-fit=cover meta — currently a no-opReported only, NOT fixed. frontend/taskdeck-web/index.html:7 has <meta name="viewport" content="width=device-width, initial-scale=1.0" /> with no viewport-fit=cover. Without it, iOS Safari reports all env(safe-area-inset-*) values as 0 and lays out within the visible area by default (so the notch/home-bar are naturally avoided). The new max(var(--td-space-4), env(safe-area-inset-*)) rule in TdDialog.vue lines 246-249 is therefore equivalent to plain var(--td-space-4) today. This is defensive (harmless, future-proof if viewport-fit=cover is ever added), not broken. Changing the viewport meta is out of scope for FE-19 — it affects every screen, not just mobile dialogs. Worth noting as a follow-up when doing the broader "secondary-view mobile sweep" that's already deferred.

  3. Dialog body doesn't scroll independently of header/footer on mobileReported only, NOT fixed. With .td-dialog { height: 100dvh; display: flex; flex-direction: column; overflow-y: auto; }, long body content causes the whole dialog to scroll, taking the header and footer with it rather than keeping them sticky. This is acceptable — footer actions remain reachable by scrolling — but not best-practice "sticky header + scrollable body + sticky footer" for native-feeling mobile dialogs. Out of scope for the merge; tracking-worthy for a future mobile polish pass.

  4. Desktop DnD preservation — Verified: all new rules are scoped inside @media (max-width: 640px). BoardCanvas.vue keeps its original flex-direction: row on desktop; HTML5 @dragstart/@dragover/@drop handlers in ColumnLane and card components are unaffected. Column ordering logic operates on the same DOM structure.

  5. @mobile Playwright flake surface — The new test uses Date.now() for unique names, .first() to avoid strict-mode collisions, and ±2/±4px tolerances on box math. Inherits the project-wide timeout: 45_000 and expect.timeout: 8_000. No retries configured in CI (retries: 0), but the assertions are deterministic (CSS-driven layout, not animation-dependent). Flake risk is no worse than existing @mobile tests.

  6. No new inline :style bindingsgit diff main..HEAD -- frontend/ shows zero style= / :style= additions. Aligned with SEC-29 (SEC-29: Migrate CSP from unsafe-inline styles to external CSS #855/sec: tighten style-src CSP (SEC-29, partial) #945) direction.

  7. No accessibility regressions beyond finding Add comprehensive Application layer test suite #3 — No new interactive elements, no hamburger/nav toggle changes, no focus-order changes outside the dialog footer. aria-modal, aria-label, tabindex, role="dialog" on TdDialog are untouched; the existing focus trap still works unchanged.

  8. SignalR reconnection is layout-independent — Media queries don't remount components. SignalR hub lifecycle is bound to BoardView, not canvas layout. Crossing the 640px breakpoint via viewport resize does not trigger reconnection churn.

CI

Before this session: 14/15 green, E2E Smoke in progress on f90ebace. After pushing d91eb5ab a fresh CI run will start. Changes are CSS-only inside a scoped <style> block plus an explanatory comment — no runtime behavior change expected.

Verdict

Ready to merge once CI on d91eb5ab settles green. One new commit this session (d91eb5ab) addresses a genuine WCAG 2.4.3 a11y regression. Two reported-not-fixed items (#4 safe-area meta, #5 sticky header/footer) are intentionally deferred as beyond FE-19 scope — they belong in the already-planned secondary-view mobile sweep.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial Code Review: PR #944 — FE-19 Mobile-Responsive Board, Toolbar, and Dialog

Reviewer: Adversarial review (manual)
Files reviewed: All 6 changed files (BoardActionRail, BoardCanvas, BoardToolbar, ColumnLane, TdDialog, mobile-responsive.spec.ts)
Context: Design tokens verified in design-tokens.css, index.html viewport meta inspected, existing mobile patterns in CaptureModal and ShellSidebar compared.


Critical (P0)

1. TdDialog.vue.td-dialog__footer > * is dead CSS due to Vue scoped style + slot boundary

File: src/components/ui/TdDialog.vue, new mobile media query block
Confidence: 95

The rule:

.td-dialog__footer > * {
  width: 100%;
  min-height: 44px;
}

In Vue 3 scoped styles, the compiler appends the component data attribute to the last simple selector in each complex selector. This compiles to approximately:

.td-dialog__footer > *[data-v-xxxxxxxx] { ... }

The <footer> is owned by TdDialog and gets the scope attribute. However, > * targets the footer children, which are slotted content from parent components (e.g., CardModal, BoardSettingsModal, LabelManager). Slotted elements do not receive TdDialog scope attribute. As a result, this rule matches nothing — the 44px tap targets and full-width stacking on footer buttons silently fail on every dialog consumer.

Fix: Use Vue :slotted() pseudo-element:

.td-dialog__footer > :slotted(*) {
  width: 100%;
  min-height: 44px;
}

This is the only scoped-styles mechanism that can reach into slot content. The project currently has zero uses of :slotted(), so this would be the first — but it is the correct pattern.


2. TdDialog.vueenv(safe-area-inset-*) requires viewport-fit=cover which is missing

File: src/components/ui/TdDialog.vue, mobile media query + index.html
Confidence: 92

The dialog mobile padding uses env(safe-area-inset-top) etc.:

padding: max(var(--td-space-4), env(safe-area-inset-top))
         max(var(--td-space-4), env(safe-area-inset-right))
         max(var(--td-space-4), env(safe-area-inset-bottom))
         max(var(--td-space-4), env(safe-area-inset-left));

However, index.html line 7 has:

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

The env(safe-area-inset-*) values are always 0 unless the viewport meta tag includes viewport-fit=cover. Without it, Safari on notched iPhones uses the "safe area" default layout where content is already inset, and env() returns 0px. The max() fallback to --td-space-4 means this is not broken, but the safe-area-inset logic is entirely non-functional — it is aspirational code that gives a false sense of notch handling.

This is also the first use of env(safe-area-inset-*) anywhere in the codebase (confirmed via grep — zero matches before this PR), which means adding viewport-fit=cover could have side effects on the app shell layout (content would extend behind the notch/home indicator globally, requiring safe-area-inset handling everywhere, not just in TdDialog).

Fix (two options):

  • Option A (minimal): Remove the env(safe-area-inset-*) references from TdDialog since they are non-functional without the viewport-fit change, and the PR description does not mention adding viewport-fit=cover. Keep the --td-space-4 padding which is sufficient.
  • Option B (complete): Add viewport-fit=cover to the viewport meta tag, but then audit all full-bleed components (AppShell, ShellSidebar, ShellTopbar, CaptureModal) to ensure they also use safe-area-inset padding. This is a larger follow-up.

Minor (P2)

3. BoardCanvas.vueoverflow-y: visible combined with overflow-x: hidden triggers scroll containment

File: src/components/board/BoardCanvas.vue, mobile media query
Confidence: 80

@media (max-width: 640px) {
  .td-board-canvas {
    height: auto;
    overflow-x: hidden;
    overflow-y: visible;
  }
}

Per the CSS overflow specification, when one axis is set to hidden and the other to visible, the visible value is computed as auto (not visible). This is a well-known spec behavior:

"If one axis uses visible and the other uses hidden, scroll, or auto, the visible value is computed as auto."

In practice, overflow-y: visible becomes overflow-y: auto here, which means the canvas gets a vertical scrollbar when content overflows rather than truly "visible" overflow that spills out to the page. This may accidentally create a nested scroll container on mobile rather than letting the page itself scroll.

Fix: Since the intent is "let the page scroll vertically, but prevent horizontal overflow", consider using overflow-x: clip instead of overflow-x: hidden. The clip value does NOT trigger the overflow interaction — it clips without establishing a scroll container. This preserves overflow-y: visible as truly visible.

overflow-x: clip;
overflow-y: visible;

Note: overflow: clip has good browser support (Safari 16+, Chrome 90+, Firefox 81+). For older browsers, overflow-x: hidden with overflow-y: auto (the computed value) may still work acceptably if the height: auto makes the container size to content anyway.


Summary of Bot Findings Assessment

Bot Finding Verdict
Back button missing min-width: 44px for tap target Addressed by the PR — the mobile media query adds min-width: 44px to __back-btn. The bot may have reviewed an earlier revision.
ColumnLane hardcodes 16px font-size False positive — the PR uses var(--td-font-lg) (the design token), not a hardcoded value. The bot may have been confused by the comment mentioning "16px".

Action Items (blocking)

  1. P0: Fix .td-dialog__footer > * to .td-dialog__footer > :slotted(*) so footer button sizing actually applies to slotted content.
  2. P0: Either add viewport-fit=cover to the viewport meta tag (with a full safe-area audit of other components) or remove the env(safe-area-inset-*) references that are currently non-functional.

Action Items (recommended)

  1. P2: Consider overflow-x: clip instead of overflow-x: hidden in BoardCanvas mobile styles to avoid the visible-becomes-auto overflow interaction.

Overall Assessment

Good PR with solid test coverage and thoughtful mobile patterns (dvh fallback, iOS zoom prevention, WCAG focus-order reasoning in the column-reverse comment). The two P0s are real issues that will silently fail in production on actual devices — the footer buttons will not get 44px sizing on any dialog, and the safe-area insets will always evaluate to 0.

TdDialog mobile footer: replace `> *` with `:deep(> *)` so the 44px
tap-target and full-width rules actually reach slotted consumer content
(Vue 3 scoped styles append [data-v-xxx] to `*`, which never matches
slot children from other components).

BoardCanvas mobile: change `overflow-x: hidden` to `overflow-x: clip`
to avoid the CSS spec quirk where `overflow-y: visible` computes to
`auto` when the cross-axis is `hidden`.
Chris0Jeky added a commit that referenced this pull request Apr 23, 2026
Update STATUS.md, IMPLEMENTATION_MASTERPLAN.md, TESTING_GUIDE.md,
MANUAL_TEST_CHECKLIST.md, AUDIT.md, and HARDENING_AND_PERFORMANCE.md
to reflect the mobile/security/legal/testing expansion wave:
FE-19 mobile responsive, SEC-29 CSP style-src, legal drafts,
TST-59 visual regression expansion, TST-60 E2E parallelization.
@Chris0Jeky Chris0Jeky merged commit a4bd654 into main Apr 23, 2026
15 checks passed
@Chris0Jeky Chris0Jeky deleted the feat/fe-19-mobile-responsive branch April 23, 2026 23:01
@github-project-automation github-project-automation Bot moved this from Pending to Done in Taskdeck Execution Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

FE-19: Responsive design for mobile breakpoints

1 participant