Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/design-system/GATES.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ because it contributed nothing.
`tests/ui-style-contract.spec.ts`) — do not re-litigate. Overlay z-index vs
named `--z-*` tokens is **implemented-blocking** in the same spec (`overlay
z-index computed tokens`); class-string assertions are forbidden; the
`rawCssZIndices` exception baseline stays at 8 in `globals.css`. Still planned
`rawCssZIndices` exception baseline stays at 4 in `globals.css`. Still planned
(PR 1–2): Tailwind conflict order · v2 cascade, ancestor **and** same-node
forms · target geometry · contrast · print token reset. Class-string assertions
are not accepted for any of these — computed values only.
Expand Down
72 changes: 45 additions & 27 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,12 @@
--disabled: #8794a8;
--focus: var(--clinical-accent);
--overlay-backdrop: rgb(4 8 14 / 56%);
/* Documented stacking ladder (TOKENS). Live overlay/popover/modal/toast plus
v2 base/raised/chrome. Do not invent rungs; z-index:30 phone stack and
z-index:2 carets have no named token and stay literal. */
--z-base: 0;
--z-raised: 10;
--z-chrome: 60;
--z-overlay: 80;
--z-popover: 95;
--z-modal: 100;
Expand Down Expand Up @@ -1250,9 +1256,9 @@ summary::-webkit-details-marker {
0 8px 24px rgb(16 24 40 / 8%);
backdrop-filter: blur(18px) saturate(140%);
transition:
border-color 180ms var(--ease-out-soft),
box-shadow 180ms var(--ease-out-soft),
transform 180ms var(--ease-spring);
border-color var(--duration-base) var(--ease-out-soft),
box-shadow var(--duration-base) var(--ease-out-soft),
transform var(--duration-base) var(--ease-spring);
}
}

Expand Down Expand Up @@ -1282,6 +1288,7 @@ summary::-webkit-details-marker {
position: absolute;
inset-inline: 0;
top: 0;
/* Below the documented ladder; no --z rung for underlay scrims. */
z-index: -1;
height: max(6.5rem, calc(var(--safe-area-top) + 5.5rem));
overflow: hidden;
Expand Down Expand Up @@ -1323,7 +1330,7 @@ summary::-webkit-details-marker {
position: fixed;
inset-inline: 0;
bottom: 0;
z-index: 0;
z-index: var(--z-base);
height: max(6.5rem, calc(var(--safe-area-bottom) + 5.25rem));
background: linear-gradient(
180deg,
Expand Down Expand Up @@ -1351,7 +1358,7 @@ summary::-webkit-details-marker {
.differentials-mobile-compare-fab {
pointer-events: none;
position: relative;
z-index: 10;
z-index: var(--z-raised);
display: flex;
width: 100%;
justify-content: center;
Expand Down Expand Up @@ -1381,6 +1388,7 @@ summary::-webkit-details-marker {
letter-spacing: -0.01em;
box-shadow: 0 18px 42px rgb(0 0 0 / 24%);
touch-action: manipulation;
/* 160ms has no duration rung — do not invent one; nearest is --duration-quick (150ms). */
transition:
transform 160ms var(--ease-spring),
background-color 160ms var(--ease-out-soft),
Expand Down Expand Up @@ -1411,7 +1419,7 @@ summary::-webkit-details-marker {
.patient-details-fab {
pointer-events: none;
position: relative;
z-index: var(--z-raised, 10);
z-index: var(--z-raised);
display: flex;
width: 100%;
justify-content: center;
Expand Down Expand Up @@ -1492,7 +1500,7 @@ summary::-webkit-details-marker {
and disabled behaviour come from the design system, per COMPONENTS.md 9.1. */
.therapy-compare-tray {
position: relative;
z-index: var(--z-raised, 10);
z-index: var(--z-raised);
display: flex;
width: 100%;
align-items: center;
Expand Down Expand Up @@ -1606,7 +1614,7 @@ summary::-webkit-details-marker {
inset-inline: 0;
bottom: 0;
top: auto;
z-index: 0;
z-index: var(--z-base);
height: var(--footer-scrim-height);
overflow: hidden;
/* Localized glass only: tint around the pill, then return to true zero at
Expand Down Expand Up @@ -1648,7 +1656,7 @@ summary::-webkit-details-marker {
opacity: 1;
visibility: visible;
transition:
opacity 180ms var(--ease-out-soft),
opacity var(--duration-base) var(--ease-out-soft),
visibility 0s linear;
}

Expand Down Expand Up @@ -1850,6 +1858,7 @@ summary::-webkit-details-marker {
position: absolute;
bottom: -0.42rem;
left: var(--mode-action-caret-left, 2.4rem);
/* Caret stacking; no --z rung between base (0) and raised (10). */
z-index: 2;
width: 0.9rem;
height: 0.9rem;
Expand All @@ -1867,6 +1876,7 @@ summary::-webkit-details-marker {
position: absolute;
top: -0.42rem;
left: var(--mode-action-caret-left, 2.4rem);
/* Caret stacking; no --z rung between base (0) and raised (10). */
z-index: 2;
width: 0.9rem;
height: 0.9rem;
Expand All @@ -1885,13 +1895,13 @@ summary::-webkit-details-marker {
}

.mode-action-surface[data-placement="up"] .mode-action-panel {
border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem;
border-bottom-left-radius: var(--radius-xl);
border-bottom-right-radius: var(--radius-xl);
}

.mode-action-surface[data-placement="down"] .mode-action-panel {
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
border-top-left-radius: var(--radius-xl);
border-top-right-radius: var(--radius-xl);
}

.mode-action-mode-option:focus-visible {
Expand Down Expand Up @@ -1921,9 +1931,9 @@ summary::-webkit-details-marker {
text-align: left;
color: var(--text-muted);
transition:
background-color 150ms var(--ease-out-soft),
border-color 150ms var(--ease-out-soft),
color 150ms var(--ease-out-soft);
background-color var(--duration-quick) var(--ease-out-soft),
border-color var(--duration-quick) var(--ease-out-soft),
color var(--duration-quick) var(--ease-out-soft);
}

.mode-action-mode-option:hover {
Expand Down Expand Up @@ -2078,10 +2088,10 @@ summary::-webkit-details-marker {
place-items: center;
border-radius: 999px;
transition:
background-color 150ms var(--ease-out-soft),
color 150ms var(--ease-out-soft),
box-shadow 150ms var(--ease-out-soft),
transform 150ms var(--ease-spring);
background-color var(--duration-quick) var(--ease-out-soft),
color var(--duration-quick) var(--ease-out-soft),
box-shadow var(--duration-quick) var(--ease-out-soft),
transform var(--duration-quick) var(--ease-spring);
}

.chat-composer-icon-button {
Expand Down Expand Up @@ -2163,6 +2173,7 @@ summary::-webkit-details-marker {
}
/* Mirrors the `motion-safe` variant: animate when the OS allows it, and also when
the in-app Motion preference explicitly opts in over an OS reduce request. */
/* 170ms has no duration rung — do not invent one; nearest is --duration-base (180ms). */
@media (prefers-reduced-motion: reduce) {
html[data-motion="full"] .mode-action-surface[data-placement="up"] {
animation: mode-action-fold-up 170ms var(--ease-out-soft) both;
Expand Down Expand Up @@ -2254,6 +2265,7 @@ summary::-webkit-details-marker {
font-weight: 700;
box-shadow: 0 2px 10px rgb(16 24 40 / 6%);
backdrop-filter: blur(14px);
/* 160ms has no duration rung — leave literal (search-chip chrome). */
transition:
border-color 160ms var(--ease-out-soft),
background-color 160ms var(--ease-out-soft),
Expand Down Expand Up @@ -2612,6 +2624,7 @@ summary::-webkit-details-marker {
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
/* 160ms has no duration rung — leave literal (suggestion-chip chrome). */
transition:
border-color 160ms var(--ease-out-soft),
background-color 160ms var(--ease-out-soft),
Expand Down Expand Up @@ -2982,6 +2995,9 @@ summary::-webkit-details-marker {
.answer-footer-search-dock[data-scroll-hidden="true"] .answer-footer-search-backdrop {
opacity: 0;
visibility: hidden;
/* Phone-chrome hide delay must stay 240ms (--duration-slow). Keep the
0ms / 240ms pair literal: mobile-chrome-paint-contract pins this string,
and 0ms delays are not token-swapped. */
transition-delay: 0ms, 240ms;
}

Expand Down Expand Up @@ -3158,7 +3174,7 @@ summary::-webkit-details-marker {
--pwa-motion-enter: 280ms;

position: fixed;
z-index: 95;
z-index: var(--z-popover);
left: max(0.5rem, var(--safe-area-left));
right: max(0.5rem, var(--safe-area-right));
bottom: var(--pwa-notice-bottom-gap);
Expand Down Expand Up @@ -4034,6 +4050,7 @@ td,
.phone-sticky-header-stack {
position: sticky;
top: 0;
/* Between raised (10) and chrome (60); no named rung. Do not retune. */
z-index: 30;
}

Expand Down Expand Up @@ -4227,9 +4244,9 @@ td,
.source-capsule-face {
border-color: var(--border);
transition:
border-color 150ms var(--ease-out-soft),
box-shadow 150ms var(--ease-out-soft),
transform 150ms var(--ease-spring);
border-color var(--duration-quick) var(--ease-out-soft),
box-shadow var(--duration-quick) var(--ease-out-soft),
transform var(--duration-quick) var(--ease-spring);
}

.source-capsule-hit:hover .source-capsule-face {
Expand Down Expand Up @@ -4258,7 +4275,7 @@ td,
.polished-scroll {
scrollbar-color: color-mix(in srgb, var(--border-strong) 65%, transparent) transparent;
scrollbar-width: thin;
transition: scrollbar-color 200ms ease;
transition: scrollbar-color var(--duration-moderate) ease;
}

.polished-scroll:hover,
Expand All @@ -4283,8 +4300,8 @@ td,
background-color: color-mix(in srgb, var(--border-strong) 60%, transparent);
background-clip: padding-box;
border: 3px solid transparent;
border-radius: 999px;
transition: background-color 150ms ease;
border-radius: var(--radius-pill);
transition: background-color var(--duration-quick) ease;
}

.polished-scroll:hover::-webkit-scrollbar-thumb {
Expand Down Expand Up @@ -4341,6 +4358,7 @@ html[data-motion="reduced"] .answer-activity-trace__sweep {

.stagger-item {
animation: cascade-fade-up var(--duration-moderate) var(--ease-out-soft) both;
/* 35ms stagger interval has no duration rung. */
animation-delay: calc(var(--stagger-index, 0) * 35ms);
}

Expand Down
8 changes: 7 additions & 1 deletion src/components/ClinicalDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,15 @@ function ClinicalDashboardContent({
if (!target) return;
setActiveHash(href);
const targetTop = target.getBoundingClientRect().top;
const top = ownsVerticalScroll(main)
const unclamped = ownsVerticalScroll(main)
? main.scrollTop + targetTop - main.getBoundingClientRect().top - 8
: window.scrollY + targetTop - 8;
// Clamp like settings-dialog: short #quotes|#images|#sources sections can
// compute a top past the runway. scrollSurface also clamps; keep the
// local clamp so this hash path does not fight use-hide-on-scroll.
const scroller = ownsVerticalScroll(main) ? main : (document.scrollingElement ?? document.documentElement);
const maxOffset = Math.max(0, scroller.scrollHeight - scroller.clientHeight);
const top = Math.min(Math.max(0, unclamped), maxOffset);
scrollSurface(main, top);
if (shouldUpdateHistory) window.history.replaceState(null, "", href);
navSyncLockRef.current = window.setTimeout(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/calculators/guided-flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ function QuestionScreen({
className="h-1.5 overflow-hidden rounded-full bg-[color:var(--surface-inset)]"
>
<div
className="h-full rounded-full bg-[color:var(--clinical-accent)] transition-[width] duration-[var(--duration-deliberate)]"
style={{ width: `${(stepIndex / total) * 100}%` }}
className="h-full w-full origin-left rounded-full bg-[color:var(--clinical-accent)] transition-transform duration-[var(--duration-deliberate)]"
style={{ transform: `scaleX(${total > 0 ? stepIndex / total : 0})` }}
/>
</div>

Expand Down
10 changes: 8 additions & 2 deletions src/components/clinical-dashboard/scroll-surface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ export function ownsVerticalScroll(element: HTMLElement) {
return overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay";
}

function clampScrollOffset(scroller: Element, target: ScrollSurfaceTarget) {
const maxOffset = Math.max(0, scroller.scrollHeight - scroller.clientHeight);
if (target === "end") return maxOffset;
return Math.min(Math.max(0, target), maxOffset);
}

export function scrollSurface(
element: HTMLElement | null,
target: ScrollSurfaceTarget,
behavior: ScrollBehavior = "auto",
) {
if (!element) return;
if (ownsVerticalScroll(element)) {
element.scrollTo({ top: target === "end" ? element.scrollHeight : target, behavior });
element.scrollTo({ top: clampScrollOffset(element, target), behavior });
return;
}
const scrollingElement = document.scrollingElement ?? document.documentElement;
window.scrollTo({ top: target === "end" ? scrollingElement.scrollHeight : target, behavior });
window.scrollTo({ top: clampScrollOffset(scrollingElement, target), behavior });
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.screen {
composes: descendantKillWithScroll from "../ward-reduced-motion.module.css";
--co-space-2: 0.125rem;
--co-space-6: 0.375rem;
--co-space-8: 0.5rem;
Expand Down Expand Up @@ -1892,13 +1893,6 @@
}
}

@media (prefers-reduced-motion: reduce) {
.screen * {
scroll-behavior: auto !important;
transition: none !important;
}
}

@media (forced-colors: active) {
.queueRowSelected {
border: 0.125rem solid Highlight;
Expand Down
7 changes: 1 addition & 6 deletions src/components/ward-management/officer/officer.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* needs is declared here, never borrowed from another module's scope.
*/
.screen {
composes: descendantKill from "../ward-reduced-motion.module.css";
--of-space-4: 0.25rem;
--of-space-6: 0.375rem;
--of-space-8: 0.5rem;
Expand Down Expand Up @@ -242,12 +243,6 @@
}
}

@media (prefers-reduced-motion: reduce) {
.screen * {
transition: none !important;
}
}

/*
* Below 40rem the icon rail is gone and `ward-sidebar.module.css`'s fixed phone bar takes its
* place. The bar is fixed rather than sticky because it has to sit above ten different shells
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* (see `officer.module.css`'s own note on the same convention).
*/
.screen {
composes: descendantKill from "../ward-reduced-motion.module.css";
--tr-space-4: 0.25rem;
--tr-space-6: 0.375rem;
--tr-space-8: 0.5rem;
Expand Down Expand Up @@ -199,12 +200,6 @@
font-weight: 700;
}

@media (prefers-reduced-motion: reduce) {
.screen * {
transition: none !important;
}
}

/*
* Below 40rem the icon rail is gone and `ward-sidebar.module.css`'s fixed phone bar takes its
* place. The bar is fixed rather than sticky because it has to sit above ten different shells
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* distinction in words (never colour alone).
*/
.demo {
composes: descendantKill from "./ward-reduced-motion.module.css";
--dc-warn: var(--warning-text);
--dc-warn-soft: var(--warning-bg);
--dc-warn-border: var(--warning-border);
Expand Down
Loading
Loading