mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 11:45:20 +02:00
feat(ui): add security banner markup + styles (approved variant A)
HTML + CSS for the canary leak / ML block banner. Structure matches the
approved mockup from /plan-design-review 2026-04-19 (variant A — centered
alert-heavy):
* Red alert-circle SVG icon (no stock shield, intentional — matches the
"serious but not scary" tone the review chose)
* "Session terminated" Satoshi Bold 18px red headline
* "— prompt injection detected from {domain}" DM Sans zinc subtitle
* Expandable "What happened" chevron button (aria-expanded/aria-controls)
* Layer list rendered in JetBrains Mono with amber tabular-nums scores
* Close X in top-right, 28px hit area, focus-visible amber outline
Enter animation: slide-down 8px + fade, 250ms, cubic-bezier(0.16,1,0.3,1) —
matches DESIGN.md motion spec. Respects `role="alert"` + `aria-live="assertive"`
so screen readers announce on appearance. Escape-to-dismiss hook is in the
JS follow-up commit.
Design tokens all via CSS variables (--error, --amber-400, --amber-500,
--zinc-*, --font-display, --font-mono, --radius-*) — already established in
the stylesheet. No new color constants introduced.
JS wiring lands in the next commit so this diff stays focused on
presentation layer only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,138 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── Security Banner ─────────────────────────────────────────────
|
||||||
|
Variant A approved in /plan-design-review 2026-04-19. Centered
|
||||||
|
alert-heavy. Fires on security_event — canary leaks + ML BLOCK
|
||||||
|
verdicts. Trust UX: layer names + confidence scores in mono so
|
||||||
|
the user can see exactly WHY the session was terminated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.security-banner {
|
||||||
|
position: relative;
|
||||||
|
padding: 20px 16px;
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(20, 20, 20, 0.98);
|
||||||
|
border-bottom: 1px solid rgba(239, 68, 68, 0.3);
|
||||||
|
animation: securityBannerEnter 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes securityBannerEnter {
|
||||||
|
from { opacity: 0; transform: translateY(-8px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 6px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--zinc-500, #71717A);
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.security-banner-close:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--zinc-300, #D4D4D8);
|
||||||
|
}
|
||||||
|
.security-banner-close:focus-visible {
|
||||||
|
outline: 2px solid var(--amber-500);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-icon {
|
||||||
|
color: var(--error);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-title {
|
||||||
|
font-family: var(--font-display, 'Satoshi', sans-serif);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--error);
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-subtitle {
|
||||||
|
font-family: var(--font-body, 'DM Sans', sans-serif);
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--zinc-400, #A1A1AA);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-expand {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
padding: 6px 12px;
|
||||||
|
color: var(--zinc-300, #D4D4D8);
|
||||||
|
font-family: var(--font-body, 'DM Sans', sans-serif);
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.security-banner-expand:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
}
|
||||||
|
.security-banner-expand:focus-visible {
|
||||||
|
outline: 2px solid var(--amber-500);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.security-banner-chevron {
|
||||||
|
transition: transform 200ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-details {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-section-label {
|
||||||
|
font-family: var(--font-mono, 'JetBrains Mono', monospace);
|
||||||
|
font-size: 10px;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--zinc-500, #71717A);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-layers {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-layer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
font-family: var(--font-mono, 'JetBrains Mono', monospace);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-layer-name {
|
||||||
|
color: var(--zinc-300, #D4D4D8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-banner-layer-score {
|
||||||
|
color: var(--amber-400);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
.conn-btn {
|
.conn-btn {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
|
|||||||
@@ -14,6 +14,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Security event banner — fires on prompt injection detection.
|
||||||
|
Variant A from /plan-design-review 2026-04-19: centered alert-heavy,
|
||||||
|
big red error icon, mono layer scores in expandable details. -->
|
||||||
|
<div class="security-banner" id="security-banner" role="alert" aria-live="assertive" style="display:none">
|
||||||
|
<button class="security-banner-close" id="security-banner-close" aria-label="Dismiss">×</button>
|
||||||
|
<div class="security-banner-icon" aria-hidden="true">
|
||||||
|
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="12"></line>
|
||||||
|
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="security-banner-title" id="security-banner-title">Session terminated</div>
|
||||||
|
<div class="security-banner-subtitle" id="security-banner-subtitle">prompt injection detected</div>
|
||||||
|
<button class="security-banner-expand" id="security-banner-expand" aria-expanded="false" aria-controls="security-banner-details">
|
||||||
|
<span>What happened</span>
|
||||||
|
<svg class="security-banner-chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="security-banner-details" id="security-banner-details" hidden>
|
||||||
|
<div class="security-banner-section-label">SECURITY LAYERS</div>
|
||||||
|
<div class="security-banner-layers" id="security-banner-layers"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Browser tab bar -->
|
<!-- Browser tab bar -->
|
||||||
<div class="browser-tabs" id="browser-tabs" style="display:none"></div>
|
<div class="browser-tabs" id="browser-tabs" style="display:none"></div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user