From 3a2b347abab88e668219ddba5c9a7aa2ff7983bf Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 11:51:46 -0600 Subject: [PATCH 01/14] Restore bubble to Maybe Stalled cards in Maybe are often out of view --- app/assets/stylesheets/card-columns.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/card-columns.css b/app/assets/stylesheets/card-columns.css index 2170b576e..501be0d3e 100644 --- a/app/assets/stylesheets/card-columns.css +++ b/app/assets/stylesheets/card-columns.css @@ -681,6 +681,7 @@ /* -------------------------------------------------------------------------- */ /* Surface a mini bubble if there are cards with bubbles inside */ + .cards--considering:has(.bubble:not([hidden])), .cards--doing.is-collapsed:has(.bubble:not([hidden])) { .cards__transition-container { --bubble-color: var(--card-color, oklch(var(--lch-blue-medium))); @@ -700,6 +701,11 @@ translate: 20% -20%; z-index: 1; } + + .cards--considering &:before { + inset: 0 auto auto 0; + translate: 100% 75%; + } } } From 2f6f4cd3f5920bfe18e5095cc052a18c61558052 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 15:47:57 -0600 Subject: [PATCH 02/14] Set reasonable maxes --- app/assets/stylesheets/popup.css | 3 ++- app/assets/stylesheets/tooltips.css | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/popup.css b/app/assets/stylesheets/popup.css index ab1e6ce74..6eb86837c 100644 --- a/app/assets/stylesheets/popup.css +++ b/app/assets/stylesheets/popup.css @@ -9,8 +9,9 @@ --popup-display: flex; inset: 0 auto auto 50%; - min-inline-size: min(25ch, calc(100vw - (var(--panel-padding) * 2))); + max-block-size: 70dvh; max-inline-size: min(55ch, calc(100vw - (var(--panel-padding) * 2))); + min-inline-size: min(25ch, calc(100vw - (var(--panel-padding) * 2))); overflow: auto; position: absolute; transform: translateX(-50%); diff --git a/app/assets/stylesheets/tooltips.css b/app/assets/stylesheets/tooltips.css index 419664fc6..f6ff54ffc 100644 --- a/app/assets/stylesheets/tooltips.css +++ b/app/assets/stylesheets/tooltips.css @@ -10,7 +10,7 @@ font-size: var(--text-x-small); font-weight: normal; inset: -1ch auto auto 50%; - max-inline-size: 50ch; + max-inline-size: min(50ch, calc(100vw - (var(--inline-space) * 2))); opacity: 0; padding: 0.25ch 1ch; transition: var(--tooltip-duration) ease-out allow-discrete; From 9cc63c6dcebce210169f3ef1344f90ced3f9b6da Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 15:48:08 -0600 Subject: [PATCH 03/14] This is too fancy, remove it --- app/javascript/controllers/dialog_controller.js | 4 ---- app/javascript/helpers/sizing_helpers.js | 12 ------------ 2 files changed, 16 deletions(-) delete mode 100644 app/javascript/helpers/sizing_helpers.js diff --git a/app/javascript/controllers/dialog_controller.js b/app/javascript/controllers/dialog_controller.js index b024bce2b..3df135c12 100644 --- a/app/javascript/controllers/dialog_controller.js +++ b/app/javascript/controllers/dialog_controller.js @@ -1,6 +1,5 @@ import { Controller } from "@hotwired/stimulus" import { orient } from "helpers/orientation_helpers" -import { limitHeightToViewport } from "helpers/sizing_helpers" export default class extends Controller { static targets = [ "dialog" ] @@ -23,8 +22,6 @@ export default class extends Controller { orient(this.dialogTarget) } - limitHeightToViewport(this.dialogTarget, this.sizingValue) - this.loadLazyFrames() this.dialogTarget.setAttribute("aria-hidden", "false") this.dispatch("show") @@ -43,7 +40,6 @@ export default class extends Controller { this.dialogTarget.setAttribute("aria-hidden", "true") this.dialogTarget.blur() orient(this.dialogTarget, false) - limitHeightToViewport(this.dialogTarget, false) this.dispatch("close") } diff --git a/app/javascript/helpers/sizing_helpers.js b/app/javascript/helpers/sizing_helpers.js deleted file mode 100644 index 4b7e99645..000000000 --- a/app/javascript/helpers/sizing_helpers.js +++ /dev/null @@ -1,12 +0,0 @@ -export function limitHeightToViewport(el, sizing = true, margin = 64) { - if (!el) return - - if (sizing) { - const rect = el.getBoundingClientRect() - const top = Math.max(rect.top, margin) - const max = Math.max(0, window.innerHeight - margin - top) - el.style.maxHeight = `${max}px` - } else { - el.style.maxHeight = "" - } -} \ No newline at end of file From ed19e6d4947ecab15f6d006cf6fdb477c9cb6609 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 15:49:42 -0600 Subject: [PATCH 04/14] Remove top orientation (it doesn't work well), make right and left classes exclusive In certain cases (board picker) both classes would be applied making a mess --- app/javascript/helpers/orientation_helpers.js | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/javascript/helpers/orientation_helpers.js b/app/javascript/helpers/orientation_helpers.js index 4e3f1a3a5..b79d66360 100644 --- a/app/javascript/helpers/orientation_helpers.js +++ b/app/javascript/helpers/orientation_helpers.js @@ -1,15 +1,18 @@ const EDGE_THRESHOLD = 16 export function orient(el, orient = true) { - const directions = [ - ["orient-left", spaceOnRight], - ["orient-right", spaceOnLeft], - ["orient-top", spaceOnBottom] - ]; + el.classList.remove("orient-left", "orient-right") - directions.forEach(([className, fn]) => - el.classList[orient && fn(el) < EDGE_THRESHOLD ? "add" : "remove"](className) - ); + if (!orient) return + + const rightSpace = spaceOnRight(el) + const leftSpace = spaceOnLeft(el) + + if (rightSpace < EDGE_THRESHOLD && rightSpace < leftSpace) { + el.classList.add("orient-left") + } else if (leftSpace < EDGE_THRESHOLD && leftSpace < rightSpace) { + el.classList.add("orient-right") + } } function spaceOnLeft(el) { @@ -19,7 +22,3 @@ function spaceOnLeft(el) { function spaceOnRight(el) { return window.innerWidth - el.getBoundingClientRect().right } - -function spaceOnBottom(el) { - return window.innerHeight - el.getBoundingClientRect().bottom -} From 4e14c0df2781a40e6f686e59986774a4e1eacd78 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 16:16:09 -0600 Subject: [PATCH 05/14] Reasonalbe max heights Use all the available space above the search/trays footer. On mobile use the full viewport height --- app/assets/stylesheets/nav.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/nav.css b/app/assets/stylesheets/nav.css index b37201e1d..6d926027b 100644 --- a/app/assets/stylesheets/nav.css +++ b/app/assets/stylesheets/nav.css @@ -75,10 +75,15 @@ 0 0.8em 0.8em oklch(var(--lch-blue-medium) / 5%); grid-template-rows: auto 1fr auto; gap: var(--nav-section-gap); + max-block-size: calc(100vh - var(--block-space) - var(--footer-height)); overflow: hidden; padding-block-end: 0; scrollbar-gutter: stable both-edges; z-index: var(--z-nav); + + @media (max-height: 668px) { + max-block-size: calc(100vh - var(--block-space)); + } } .nav__scroll-container { From b93224b45c644f8e830ad749d5edc592440f294f Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 16:22:33 -0600 Subject: [PATCH 06/14] Smaller on mobile It really takes up a lot of space in landscape --- app/assets/stylesheets/nav.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/nav.css b/app/assets/stylesheets/nav.css index 6d926027b..2753a82c5 100644 --- a/app/assets/stylesheets/nav.css +++ b/app/assets/stylesheets/nav.css @@ -214,5 +214,9 @@ padding: 1.5ch; text-align: center; z-index: 1; + + @media (max-height: 668px) { + font-size: var(--text-x-small); + } } } From a1fcabf01e74c9ff07cbd8057c2e8a50d9605d4d Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 16:33:32 -0600 Subject: [PATCH 07/14] Add env constaints for mobile layout In theory, this prevents overlapping device elements like the dynamic island in landscape --- app/assets/stylesheets/bar.css | 4 +++- app/assets/stylesheets/layout.css | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/bar.css b/app/assets/stylesheets/bar.css index 1aa2494de..077412a4d 100644 --- a/app/assets/stylesheets/bar.css +++ b/app/assets/stylesheets/bar.css @@ -11,7 +11,9 @@ inset: auto 0 0 0; max-block-size: 100%; padding-block: var(--block-space) calc(var(--block-space) + env(safe-area-inset-bottom)); - padding-inline: calc(var(--tray-size) + calc(var(--inline-space) * 3)); + padding-inline: + calc(var(--tray-size) + calc(var(--inline-space) * 3) + env(safe-area-inset-left)) + calc(var(--tray-size) + calc(var(--inline-space) * 3) + env(safe-area-inset-right)); place-content: center; position: fixed; view-transition-name: bar; diff --git a/app/assets/stylesheets/layout.css b/app/assets/stylesheets/layout.css index f4157a270..e82992dfc 100644 --- a/app/assets/stylesheets/layout.css +++ b/app/assets/stylesheets/layout.css @@ -12,7 +12,9 @@ inline-size: 100dvw; margin-inline: auto; max-inline-size: 100dvw; - padding-inline: var(--main-padding); + padding-inline: + calc(var(--main-padding) + env(safe-area-inset-left)) + calc(var(--main-padding) + env(safe-area-inset-right)); text-align: center; } From 789c00b87f0678797894b05036a0dcf3008dba1a Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 16:37:50 -0600 Subject: [PATCH 08/14] Dark theme color isn't black I *think* we don't need the theme color in the manifest since it doesn't have dark mode support anyway --- app/views/layouts/shared/_head.html.erb | 2 +- app/views/pwa/manifest.json.erb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/shared/_head.html.erb b/app/views/layouts/shared/_head.html.erb index 550b61eaa..ecee1abc0 100644 --- a/app/views/layouts/shared/_head.html.erb +++ b/app/views/layouts/shared/_head.html.erb @@ -7,7 +7,7 @@ <% end %> - + <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= tag.meta name: "current-user-id", content: Current.user.id if Current.user %> diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb index fc7777836..2f268a7d4 100644 --- a/app/views/pwa/manifest.json.erb +++ b/app/views/pwa/manifest.json.erb @@ -36,7 +36,5 @@ "url": "<%= root_path %>", "icons": [{ "src": "<%= image_url("activity.svg") %>", "sizes": "any" }] } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff" + ] } From cfc61c318dc37a5e4289f8c07fc5fc96eed8b4ec Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 17:18:44 -0600 Subject: [PATCH 09/14] Nothing should cover the bar, trays or search results --- app/assets/stylesheets/_global.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/_global.css b/app/assets/stylesheets/_global.css index 9c0b30f0f..414d4c696 100644 --- a/app/assets/stylesheets/_global.css +++ b/app/assets/stylesheets/_global.css @@ -74,11 +74,11 @@ --z-events-column-header: 1; --z-events-day-header: 3; --z-popup: 10; - --z-bar: 20; - --z-tray: 21; --z-nav: 30; --z-flash: 40; --z-tooltip: 50; + --z-bar: 60; + --z-tray: 61; /* OKLCH colors: Fixed */ --lch-black: 0% 0 0; From 74cd88d6268ce26b4c9b025cdd961dad0d1a79c0 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 17:19:01 -0600 Subject: [PATCH 10/14] Always display the cancel button, `esc` isn't obvious --- app/assets/stylesheets/search.css | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/assets/stylesheets/search.css b/app/assets/stylesheets/search.css index bf6b44c07..60f9f05f8 100644 --- a/app/assets/stylesheets/search.css +++ b/app/assets/stylesheets/search.css @@ -50,13 +50,7 @@ summary { .search__reset { --btn-background: var(--color-terminal-bg); - --btn-size: 1.25lh; - - @media (min-width: 800px) { - .search__input:placeholder-shown ~ & { - opacity: 0; - } - } + --btn-size: 1.5lh; } From 0c05694a3c001c96bdee0ec8c8f4d92f3b302b5c Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 17:48:02 -0600 Subject: [PATCH 11/14] This seems to have caused the unwanted effect of not focusing the field in Mobile Safari Looks like it regressed in https://github.com/basecamp/fizzy/commit/250935530f86eb628d6b54ec6b7212dac9e76f8b. Let's see if we can get focus in Safari without sacrificing select in other browsers by simply moving the nextFrame() call --- app/javascript/controllers/bar_controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/controllers/bar_controller.js b/app/javascript/controllers/bar_controller.js index a1fb7e144..5e72e4e07 100644 --- a/app/javascript/controllers/bar_controller.js +++ b/app/javascript/controllers/bar_controller.js @@ -65,8 +65,8 @@ export default class extends Controller { const autofocusElement = element.querySelector("[autofocus]") - await nextFrame() autofocusElement?.focus() + await nextFrame() autofocusElement?.select() } From b45554966fd93e23e51b33a7a8a6d22e1533a0f9 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Mon, 24 Nov 2025 17:48:21 -0600 Subject: [PATCH 12/14] Lose the second close button --- app/assets/stylesheets/search.css | 12 +----------- app/views/searches/_results.html.erb | 7 ------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/app/assets/stylesheets/search.css b/app/assets/stylesheets/search.css index 60f9f05f8..c5390e793 100644 --- a/app/assets/stylesheets/search.css +++ b/app/assets/stylesheets/search.css @@ -57,22 +57,12 @@ summary { /* Content /* ------------------------------------------------------------------------ */ - .search__header { - align-items: center; - display: flex; - inset: 0 0 auto 0; - justify-content: flex-end; - padding: var(--block-space-half) var(--block-space); - pointer-events: none; - position: absolute; - } - .search__list { display: grid; gap: var(--block-space); list-style: none; margin: 0 auto; - max-inline-size: 80ch; + max-inline-size: min(80ch, 100%); padding: 0; text-align: start; } diff --git a/app/views/searches/_results.html.erb b/app/views/searches/_results.html.erb index 7cdd92df9..ca82d65ee 100644 --- a/app/views/searches/_results.html.erb +++ b/app/views/searches/_results.html.erb @@ -1,10 +1,3 @@ -
- -
-