From 8f4c48251e4704ccbe2b11ce69554f84e8237a79 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 12 Jun 2026 20:50:18 +0200 Subject: [PATCH 1/8] CC-101: Improve Component Library Mobile Support Resolves CC-101 --- src/components/AppNavigation/AppNavigation.module.scss | 4 ++-- .../MobileNavigation/MobileNavigation.module.scss | 4 ++-- src/components/DialogManager/Dialog.module.scss | 6 ++++-- src/components/Drawer/Drawer.module.scss | 4 ++-- src/components/InputPanel/InputPanel.module.scss | 9 ++++++--- .../Menu/components/MobileMenu/MobileMenu.module.scss | 1 + .../Tabs/components/TabsList/TabsList.module.scss | 6 +++--- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/AppNavigation/AppNavigation.module.scss b/src/components/AppNavigation/AppNavigation.module.scss index 5c8f474..93e8b24 100644 --- a/src/components/AppNavigation/AppNavigation.module.scss +++ b/src/components/AppNavigation/AppNavigation.module.scss @@ -17,7 +17,7 @@ box-sizing: border-box; width: 100vw; - height: calc(var(--app-bar-height) + var(--bleed)); + height: calc(var(--app-bar-height) + var(--bleed) + env(safe-area-inset-top)); padding-top: var(--bleed); &-content { @@ -27,7 +27,7 @@ box-sizing: border-box; margin: 0 auto; - padding: 0.75rem var(--page-padding) calc(0.75rem - var(--border-width)); + padding: calc(0.75rem + env(safe-area-inset-top)) var(--page-padding) calc(0.75rem - var(--border-width)); &[data-layout="mobile"] { grid-template-areas: "navigation logo secondary"; diff --git a/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss b/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss index 1cf387d..2e5754e 100644 --- a/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss +++ b/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss @@ -32,7 +32,7 @@ display: grid; grid-template-areas: "header" "routes"; grid-template-columns: 1fr; - grid-template-rows: var(--app-bar-height) 1fr; + grid-template-rows: calc(var(--app-bar-height) + env(safe-area-inset-top)) 1fr; width: 20rem; max-width: calc(100vw - 2rem); @@ -55,7 +55,7 @@ grid-area: header; flex-shrink: 0; justify-content: space-between; - padding: 0 var(--padding-x); + padding: env(safe-area-inset-top) var(--padding-x) 0; } &-scroll-area { diff --git a/src/components/DialogManager/Dialog.module.scss b/src/components/DialogManager/Dialog.module.scss index dd0f67e..4d7df42 100644 --- a/src/components/DialogManager/Dialog.module.scss +++ b/src/components/DialogManager/Dialog.module.scss @@ -31,7 +31,9 @@ } &-popup { - --dialog-usable-height: calc(100dvh - (2 * var(--page-padding))); + --dialog-offset-top: calc(var(--page-padding) + env(safe-area-inset-top)); + --dialog-offset-bottom: calc(var(--page-padding) + env(safe-area-inset-bottom)); + --dialog-usable-height: calc(100dvh - (var(--dialog-offset-top) + var(--dialog-offset-bottom))); --dialog-usable-width: calc(100dvw - (2 * var(--page-padding))); --dialog-max-height: min(var(--dialog-user-max-height, 100dvh), var(--dialog-usable-height)); --dialog-max-width: min(var(--dialog-user-max-width, 100dvw), var(--dialog-usable-width)); @@ -39,7 +41,7 @@ @include shadows.elevated; position: fixed; - top: 50%; + top: calc((var(--dialog-usable-height) / 2) + var(--dialog-offset-top)); left: 50%; transform: translate(-50%, -50%) scale(calc(1 - 0.1 * var(--nested-dialogs))); translate: 0 calc(0px + 1.25rem * var(--nested-dialogs)); diff --git a/src/components/Drawer/Drawer.module.scss b/src/components/Drawer/Drawer.module.scss index e474d93..472eafa 100644 --- a/src/components/Drawer/Drawer.module.scss +++ b/src/components/Drawer/Drawer.module.scss @@ -245,7 +245,7 @@ grid-area: header; justify-content: space-between; - padding: var(--modal-padding) var(--modal-padding) 1rem; + padding: calc(var(--modal-padding) + env(safe-area-inset-top)) var(--modal-padding) 1rem; &-close { margin: -0.25rem; @@ -264,7 +264,7 @@ flex-direction: column; min-height: 0; - padding: 0 var(--modal-padding) var(--modal-padding); + padding: 0 var(--modal-padding) calc(var(--modal-padding) + env(safe-area-inset-bottom)); &[data-padding="false"] { padding: 0; diff --git a/src/components/InputPanel/InputPanel.module.scss b/src/components/InputPanel/InputPanel.module.scss index dfae7bd..2b2dc3d 100644 --- a/src/components/InputPanel/InputPanel.module.scss +++ b/src/components/InputPanel/InputPanel.module.scss @@ -51,15 +51,18 @@ } &-popup { - --input-panel-usable-height: calc(100dvh - 2 * var(--page-padding)); - --input-panel-usable-width: calc(100dvw - 2 * var(--page-padding)); + --input-panel-offset-top: calc(var(--page-padding) + env(safe-area-inset-top)); + --input-panel-offset-bottom: calc(var(--page-padding) + env(safe-area-inset-bottom)); + --input-panel-usable-height: calc(100dvh - (var(--input-panel-offset-top) + var(--input-panel-offset-bottom))); + --input-panel-usable-width: calc(100dvw - (2 * var(--page-padding))); --input-panel-max-height: min(var(--input-panel-user-max-height, 100dvh), var(--input-panel-usable-height)); --input-panel-max-width: min(var(--input-panel-user-max-width, 100dvw), var(--input-panel-usable-width)); will-change: transform; position: fixed; - inset: 50% auto auto 50%; + top: calc((var(--input-panel-usable-height) / 2) + var(--input-panel-offset-top)); + left: 50%; transform: translate(-50%, -50%); overflow: hidden; diff --git a/src/components/Menu/components/MobileMenu/MobileMenu.module.scss b/src/components/Menu/components/MobileMenu/MobileMenu.module.scss index 41e81f7..69ad265 100644 --- a/src/components/Menu/components/MobileMenu/MobileMenu.module.scss +++ b/src/components/Menu/components/MobileMenu/MobileMenu.module.scss @@ -57,6 +57,7 @@ box-sizing: border-box; width: 100%; max-width: 24rem; + max-height: calc(100dvh - env(safe-area-inset-top, 0px)); padding: 0 1rem calc(2rem + env(safe-area-inset-bottom, 0px)); outline: 0; diff --git a/src/components/Tabs/components/TabsList/TabsList.module.scss b/src/components/Tabs/components/TabsList/TabsList.module.scss index 0bf4f1b..d8ac9a3 100644 --- a/src/components/Tabs/components/TabsList/TabsList.module.scss +++ b/src/components/Tabs/components/TabsList/TabsList.module.scss @@ -31,9 +31,9 @@ left: 0; width: 100%; - height: var(--app-bar-height); - min-height: var(--app-bar-height); - padding: calc(var(--spacing) - var(--border-width)) 0 var(--spacing); + height: calc(var(--app-bar-height) + env(safe-area-inset-bottom)); + min-height: calc(var(--app-bar-height) + env(safe-area-inset-bottom)); + padding: calc(var(--spacing) - var(--border-width)) 0 calc(var(--spacing) + env(safe-area-inset-bottom)); background-color: unset; } From 68e5904441f028a5794baee44f8b0542e50b2b4f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 12 Jun 2026 21:11:25 +0200 Subject: [PATCH 2/8] PR Feedback --- .../AppNavigation/AppNavigation.module.scss | 3 +- .../MobileNavigation.module.scss | 4 +- .../DialogManager/Dialog.module.scss | 8 +-- src/components/Drawer/Drawer.module.scss | 54 ++++++++++++++++++- .../InputPanel/InputPanel.module.scss | 6 ++- .../MobileMenu/MobileMenu.module.scss | 3 +- .../components/TabsList/TabsList.module.scss | 3 +- 7 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/components/AppNavigation/AppNavigation.module.scss b/src/components/AppNavigation/AppNavigation.module.scss index 93e8b24..bf60bbd 100644 --- a/src/components/AppNavigation/AppNavigation.module.scss +++ b/src/components/AppNavigation/AppNavigation.module.scss @@ -27,7 +27,8 @@ box-sizing: border-box; margin: 0 auto; - padding: calc(0.75rem + env(safe-area-inset-top)) var(--page-padding) calc(0.75rem - var(--border-width)); + /* stylelint-disable-next-line @stylistic/max-line-length */ + padding: calc(0.75rem + env(safe-area-inset-top)) calc(var(--page-padding) + env(safe-area-inset-right)) calc(0.75rem - var(--border-width)) calc(var(--page-padding) + env(safe-area-inset-left)); &[data-layout="mobile"] { grid-template-areas: "navigation logo secondary"; diff --git a/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss b/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss index 2e5754e..87639f6 100644 --- a/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss +++ b/src/components/AppNavigation/components/MobileNavigation/MobileNavigation.module.scss @@ -55,7 +55,7 @@ grid-area: header; flex-shrink: 0; justify-content: space-between; - padding: env(safe-area-inset-top) var(--padding-x) 0; + padding: env(safe-area-inset-top) var(--padding-x) 0 calc(var(--padding-x) + env(safe-area-inset-left)); } &-scroll-area { @@ -66,7 +66,7 @@ display: flex; flex-direction: column; gap: 1rem; - padding: 1rem var(--padding-x); + padding: 1rem var(--padding-x) 1rem calc(var(--padding-x) + env(safe-area-inset-left)); } } } diff --git a/src/components/DialogManager/Dialog.module.scss b/src/components/DialogManager/Dialog.module.scss index 4d7df42..82544f4 100644 --- a/src/components/DialogManager/Dialog.module.scss +++ b/src/components/DialogManager/Dialog.module.scss @@ -33,8 +33,10 @@ &-popup { --dialog-offset-top: calc(var(--page-padding) + env(safe-area-inset-top)); --dialog-offset-bottom: calc(var(--page-padding) + env(safe-area-inset-bottom)); + --dialog-offset-left: calc(var(--page-padding) + env(safe-area-inset-left)); + --dialog-offset-right: calc(var(--page-padding) + env(safe-area-inset-right)); --dialog-usable-height: calc(100dvh - (var(--dialog-offset-top) + var(--dialog-offset-bottom))); - --dialog-usable-width: calc(100dvw - (2 * var(--page-padding))); + --dialog-usable-width: calc(100dvw - (var(--dialog-offset-left) + var(--dialog-offset-right))); --dialog-max-height: min(var(--dialog-user-max-height, 100dvh), var(--dialog-usable-height)); --dialog-max-width: min(var(--dialog-user-max-width, 100dvw), var(--dialog-usable-width)); @@ -42,7 +44,7 @@ position: fixed; top: calc((var(--dialog-usable-height) / 2) + var(--dialog-offset-top)); - left: 50%; + left: calc((var(--dialog-usable-width) / 2) + var(--dialog-offset-left)); transform: translate(-50%, -50%) scale(calc(1 - 0.1 * var(--nested-dialogs))); translate: 0 calc(0px + 1.25rem * var(--nested-dialogs)); @@ -54,7 +56,7 @@ box-sizing: border-box; width: max-content; - min-width: min(calc(100dvw - (2 * var(--page-padding))), 24rem); + min-width: min(var(--dialog-usable-width), 24rem); max-width: var(--dialog-max-width); max-height: var(--dialog-max-height); padding: var(--modal-padding) 0; diff --git a/src/components/Drawer/Drawer.module.scss b/src/components/Drawer/Drawer.module.scss index 472eafa..b04d49b 100644 --- a/src/components/Drawer/Drawer.module.scss +++ b/src/components/Drawer/Drawer.module.scss @@ -73,6 +73,10 @@ &-popup { --bleed: 3rem; --stack-scale: calc(1 - 0.05 * var(--nested-drawers, 0)); + --inset-top: calc(var(--modal-padding) + env(safe-area-inset-top)); + --inset-right: calc(var(--modal-padding) + env(safe-area-inset-right)); + --inset-bottom: calc(var(--modal-padding) + env(safe-area-inset-bottom)); + --inset-left: calc(var(--modal-padding) + env(safe-area-inset-left)); @include shadows.elevated; @@ -245,7 +249,30 @@ grid-area: header; justify-content: space-between; - padding: calc(var(--modal-padding) + env(safe-area-inset-top)) var(--modal-padding) 1rem; + padding: var(--modal-padding) var(--modal-padding) 1rem; + + // Only inset for the safe area on edges where this drawer's popup actually touches the + // screen edge for its anchor direction. + .drawer-popup[data-swipe-direction="up"] & { + padding-top: var(--inset-top); + padding-right: var(--inset-right); + padding-left: var(--inset-left); + } + + .drawer-popup[data-swipe-direction="down"] & { + padding-right: var(--inset-right); + padding-left: var(--inset-left); + } + + .drawer-popup[data-swipe-direction="left"] & { + padding-top: var(--inset-top); + padding-left: var(--inset-left); + } + + .drawer-popup[data-swipe-direction="right"] & { + padding-top: var(--inset-top); + padding-right: var(--inset-right); + } &-close { margin: -0.25rem; @@ -264,7 +291,30 @@ flex-direction: column; min-height: 0; - padding: 0 var(--modal-padding) calc(var(--modal-padding) + env(safe-area-inset-bottom)); + padding: 0 var(--modal-padding) var(--modal-padding); + + // Only inset for the safe area on edges where this drawer's popup actually touches the + // screen edge for its anchor direction. + .drawer-popup[data-swipe-direction="up"] & { + padding-right: var(--inset-right); + padding-left: var(--inset-left); + } + + .drawer-popup[data-swipe-direction="down"] & { + padding-right: var(--inset-right); + padding-bottom: var(--inset-bottom); + padding-left: var(--inset-left); + } + + .drawer-popup[data-swipe-direction="left"] & { + padding-bottom: var(--inset-bottom); + padding-left: var(--inset-left); + } + + .drawer-popup[data-swipe-direction="right"] & { + padding-right: var(--inset-right); + padding-bottom: var(--inset-bottom); + } &[data-padding="false"] { padding: 0; diff --git a/src/components/InputPanel/InputPanel.module.scss b/src/components/InputPanel/InputPanel.module.scss index 2b2dc3d..b13e7e3 100644 --- a/src/components/InputPanel/InputPanel.module.scss +++ b/src/components/InputPanel/InputPanel.module.scss @@ -53,8 +53,10 @@ &-popup { --input-panel-offset-top: calc(var(--page-padding) + env(safe-area-inset-top)); --input-panel-offset-bottom: calc(var(--page-padding) + env(safe-area-inset-bottom)); + --input-panel-offset-left: calc(var(--page-padding) + env(safe-area-inset-left)); + --input-panel-offset-right: calc(var(--page-padding) + env(safe-area-inset-right)); --input-panel-usable-height: calc(100dvh - (var(--input-panel-offset-top) + var(--input-panel-offset-bottom))); - --input-panel-usable-width: calc(100dvw - (2 * var(--page-padding))); + --input-panel-usable-width: calc(100dvw - (var(--input-panel-offset-left) + var(--input-panel-offset-right))); --input-panel-max-height: min(var(--input-panel-user-max-height, 100dvh), var(--input-panel-usable-height)); --input-panel-max-width: min(var(--input-panel-user-max-width, 100dvw), var(--input-panel-usable-width)); @@ -62,7 +64,7 @@ position: fixed; top: calc((var(--input-panel-usable-height) / 2) + var(--input-panel-offset-top)); - left: 50%; + left: calc((var(--input-panel-usable-width) / 2) + var(--input-panel-offset-left)); transform: translate(-50%, -50%); overflow: hidden; diff --git a/src/components/Menu/components/MobileMenu/MobileMenu.module.scss b/src/components/Menu/components/MobileMenu/MobileMenu.module.scss index 69ad265..a8d17d6 100644 --- a/src/components/Menu/components/MobileMenu/MobileMenu.module.scss +++ b/src/components/Menu/components/MobileMenu/MobileMenu.module.scss @@ -58,7 +58,8 @@ width: 100%; max-width: 24rem; max-height: calc(100dvh - env(safe-area-inset-top, 0px)); - padding: 0 1rem calc(2rem + env(safe-area-inset-bottom, 0px)); + /* stylelint-disable-next-line @stylistic/max-line-length */ + padding: 0 calc(1rem + env(safe-area-inset-right, 0px)) calc(2rem + env(safe-area-inset-bottom, 0px)) calc(1rem + env(safe-area-inset-left, 0px)); outline: 0; diff --git a/src/components/Tabs/components/TabsList/TabsList.module.scss b/src/components/Tabs/components/TabsList/TabsList.module.scss index d8ac9a3..bb508d9 100644 --- a/src/components/Tabs/components/TabsList/TabsList.module.scss +++ b/src/components/Tabs/components/TabsList/TabsList.module.scss @@ -43,7 +43,8 @@ height: 100%; max-height: 100%; margin: 0 auto; - padding: 0 var(--page-padding); + /* stylelint-disable-next-line @stylistic/max-line-length */ + padding: 0 calc(var(--page-padding) + env(safe-area-inset-right)) 0 calc(var(--page-padding) + env(safe-area-inset-left)); > button { height: 100%; From eb35f32ee660ad4ebeeb532194fc2673c62f2963 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 12 Jun 2026 21:54:50 +0200 Subject: [PATCH 3/8] PR Feedback --- src/components/AppNavigation/AppNavigation.module.scss | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/AppNavigation/AppNavigation.module.scss b/src/components/AppNavigation/AppNavigation.module.scss index bf60bbd..5be57f6 100644 --- a/src/components/AppNavigation/AppNavigation.module.scss +++ b/src/components/AppNavigation/AppNavigation.module.scss @@ -18,7 +18,9 @@ box-sizing: border-box; width: 100vw; height: calc(var(--app-bar-height) + var(--bleed) + env(safe-area-inset-top)); - padding-top: var(--bleed); + padding-top: calc(var(--bleed) + env(safe-area-inset-top)); + padding-right: env(safe-area-inset-right); + padding-left: env(safe-area-inset-left); &-content { display: grid; @@ -27,8 +29,7 @@ box-sizing: border-box; margin: 0 auto; - /* stylelint-disable-next-line @stylistic/max-line-length */ - padding: calc(0.75rem + env(safe-area-inset-top)) calc(var(--page-padding) + env(safe-area-inset-right)) calc(0.75rem - var(--border-width)) calc(var(--page-padding) + env(safe-area-inset-left)); + padding: 0.75rem var(--page-padding) calc(0.75rem - var(--border-width)); &[data-layout="mobile"] { grid-template-areas: "navigation logo secondary"; From 036dde7e99bd4af7348c15a36839626185799377 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 12 Jun 2026 23:32:25 +0200 Subject: [PATCH 4/8] Fix