fix(ui): keep the security notice card reachable and readable everywhere

- The card is capped to the room below the toast stack's top and
  scrolls inside itself. The stack is fixed and cannot scroll, so on a
  phone in landscape the update toast's buttons sat below the screen
  with only the × left, which records the notice unread. The accent bar
  becomes a background layer, which stays put while the content scrolls.
- Small amber text and the primary button use --notice-ink, darkened a
  step on the four light skins, where --yellow measured 3.9 to 4.4:1 for
  the 10.6px eyebrow (now 5.1 or better, the button label 5.7 or better).
- The update toast that carries a notice frames itself in the accent
  colour and leaves the amber to the warning section inside it.
- The × uses --text-dim (2.4:1 on og before, 5.5 now).
- Phones: every button fills its row (a wrapped Dismiss no longer sits
  stray at the left) and is 44px tall, and the × gets a 44px box.
- The title balances its lines, and the card matches the plain toast's
  420px so a toast stacked under it lines up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-18 09:30:13 +02:00
1 parent 5e405bbbb9
commit ee6b115023
1 file changed
+49 -25
+49 -25
View File
@@ -9726,20 +9726,30 @@ kbd {
/* Security notice card (security-notices.json; showSecurityNotice / showUpdateNotice in /* Security notice card (security-notices.json; showSecurityNotice / showUpdateNotice in
panels-ui.js). Its own card in the toast stack, never the plain pill, and in the SAME panels-ui.js). Its own card in the toast stack, never the plain pill, and in the SAME
container, so it shares the toasts' position and z-index. Theme tokens only, so every skin container, so it shares the toasts' position and z-index. Theme tokens only, so every skin
reads: the light skins' --yellow is already a dark amber that holds on white, and the reads. Small amber text and the primary button use --notice-ink: --yellow on the dark
primary button's ink is --bg-card (dark ink on the dark skins' light amber, white ink on skins, and on the light skins --yellow darkened a step, because their --yellow alone is
the light skins' dark amber). The entrance animates opacity and transform only, and under 4.5:1 for 11-12px text on white (3.9 to 4.4 measured). The primary button's label is
nothing moves under prefers-reduced-motion. */ --bg-card (dark on the dark skins' light amber, white on the light skins' dark amber). The
entrance animates opacity and transform only, and nothing moves under
prefers-reduced-motion.
The card is capped to the room below the stack's top and scrolls inside itself: the
container is fixed and does not scroll, so on a short screen (a phone in landscape) the
buttons at the bottom were otherwise off-screen and unreachable. The accent bar down the
left edge (amber running into red) is therefore a background layer, not a positioned
child: a background stays put while the content scrolls. */
.security-notice { .security-notice {
position: relative; --notice-ink: var(--yellow);
display: grid; display: grid;
grid-template-columns: auto minmax(0, 1fr) auto; grid-template-columns: auto minmax(0, 1fr) auto;
column-gap: 0.75rem; column-gap: 0.75rem;
align-items: start; align-items: start;
box-sizing: border-box; box-sizing: border-box;
width: min(27rem, calc(100vw - 1rem)); width: min(420px, calc(100vw - 1rem));
max-height: calc(var(--app-height, 100dvh) - var(--header-height) - 1rem);
padding: 0.85rem 0.55rem 0.9rem 1.05rem; padding: 0.85rem 0.55rem 0.9rem 1.05rem;
background: background:
linear-gradient(180deg, var(--yellow), var(--red)) left top / 4px 100% no-repeat,
linear-gradient(180deg, color-mix(in srgb, var(--yellow) 9%, transparent), transparent 6rem), linear-gradient(180deg, color-mix(in srgb, var(--yellow) 9%, transparent), transparent 6rem),
var(--bg-card); var(--bg-card);
border: 1px solid color-mix(in srgb, var(--yellow) 32%, var(--border)); border: 1px solid color-mix(in srgb, var(--yellow) 32%, var(--border));
@@ -9748,7 +9758,9 @@ kbd {
color: var(--text); color: var(--text);
font-size: 0.8rem; font-size: 0.8rem;
line-height: 1.45; line-height: 1.45;
overflow: hidden; overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
pointer-events: auto; pointer-events: auto;
opacity: 0; opacity: 0;
transform: translateY(-6px) scale(0.985); transform: translateY(-6px) scale(0.985);
@@ -9760,15 +9772,17 @@ kbd {
transform: none; transform: none;
} }
/* The accent bar: amber running into red down the left edge. */ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) .security-notice {
.security-notice::before { --notice-ink: color-mix(in srgb, var(--yellow) 85%, black);
content: ''; }
position: absolute;
top: 0; /* The update toast carrying a notice is still an update: its frame takes the accent colour,
bottom: 0; and the amber is left to the warning section inside it. */
left: 0; .security-notice.update-notice {
width: 4px; background:
background: linear-gradient(180deg, var(--yellow), var(--red)); linear-gradient(var(--accent), var(--accent)) left top / 4px 100% no-repeat,
var(--bg-card);
border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
} }
.security-notice-icon { .security-notice-icon {
@@ -9778,7 +9792,7 @@ kbd {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
border-radius: 50%; border-radius: 50%;
color: var(--yellow); color: var(--notice-ink);
background: color-mix(in srgb, var(--yellow) 15%, transparent); background: color-mix(in srgb, var(--yellow) 15%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--yellow) 30%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--yellow) 30%, transparent);
} }
@@ -9804,7 +9818,7 @@ kbd {
} }
.security-notice-eyebrow { .security-notice-eyebrow {
color: var(--yellow); color: var(--notice-ink);
font-size: 0.66rem; font-size: 0.66rem;
font-weight: 700; font-weight: 700;
letter-spacing: 0.09em; letter-spacing: 0.09em;
@@ -9818,6 +9832,7 @@ kbd {
font-weight: 700; font-weight: 700;
line-height: 1.3; line-height: 1.3;
overflow-wrap: anywhere; overflow-wrap: anywhere;
text-wrap: balance;
} }
.security-notice-body, .security-notice-body,
@@ -9850,7 +9865,7 @@ kbd {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.4rem; gap: 0.4rem;
color: var(--yellow); color: var(--notice-ink);
} }
.security-notice-section-head svg { .security-notice-section-head svg {
@@ -9891,12 +9906,12 @@ kbd {
padding: 0.45rem 0.85rem; padding: 0.45rem 0.85rem;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: var(--btn-radius); border-radius: var(--btn-radius);
background: var(--yellow); background: var(--notice-ink);
color: var(--bg-card); color: var(--bg-card);
} }
.security-notice-primary:hover { .security-notice-primary:hover {
background: color-mix(in srgb, var(--yellow) 86%, var(--text)); background: color-mix(in srgb, var(--notice-ink) 86%, var(--text));
} }
.security-notice-secondary { .security-notice-secondary {
@@ -9936,7 +9951,7 @@ kbd {
border: none; border: none;
border-radius: 6px; border-radius: 6px;
background: none; background: none;
color: var(--text-muted); color: var(--text-dim);
font-size: 1.15rem; font-size: 1.15rem;
line-height: 1; line-height: 1;
cursor: pointer; cursor: pointer;
@@ -9952,15 +9967,24 @@ kbd {
outline-offset: 2px; outline-offset: 2px;
} }
/* Phones: the full width minus the stack's 0.5rem gutters, and the notice's own button /* Phones: the full width minus the stack's 0.5rem gutters. Every button grows to fill its
takes the room left beside Dismiss (wrapping under it when the label is long). */ row (so one that wraps fills its own instead of sitting stray at the left) and is at least
--touch-target-min tall; the × gets a touch-sized box too, its negative margin keeping the
glyph where it sits on a desktop. */
@media (max-width: 599px) { @media (max-width: 599px) {
.security-notice { .security-notice {
width: calc(100vw - 1rem); width: calc(100vw - 1rem);
} }
.security-notice-primary { .security-notice-actions > button {
flex: 1 1 auto; flex: 1 1 auto;
min-height: var(--touch-target-min);
}
.security-notice-close {
width: var(--touch-target-min);
height: var(--touch-target-min);
margin: -0.75rem -0.5rem 0 0;
} }
} }