Spec, stylesheet, glitch script, Claude Code skill and the design canvas source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
229 lines
7.5 KiB
CSS
229 lines
7.5 KiB
CSS
/* Night Grid — dark neon HUD style for dashboards.
|
||
Guide: https://claude.ai/artifact/AT2mCxMkeLFzkife1rAuLk
|
||
Fonts: <link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
||
Random glitch: <script src="night-grid.js" defer></script> */
|
||
|
||
:root {
|
||
--ng-void: #050508; /* page ground */
|
||
--ng-panel: #0E1016; /* cards, panels */
|
||
--ng-raise: #1C2029; /* tracks, grid, hover */
|
||
--ng-line: #2A2F3A; /* 1px borders */
|
||
--ng-text: #E6E8EC;
|
||
--ng-soft: #C3C8D1; /* secondary body text */
|
||
--ng-dim: #8A93A3; /* labels */
|
||
--ng-yellow: #FCEE0A; /* action, focus */
|
||
--ng-cyan: #02D7F2; /* info, live data */
|
||
--ng-red: #FF003C; /* danger fill/border */
|
||
--ng-red-txt: #FF3B5C; /* danger text */
|
||
--ng-green: #00FF9F; /* ok */
|
||
--ng-font-ui: 'Rajdhani', sans-serif;
|
||
--ng-font-data: 'JetBrains Mono', monospace;
|
||
--ng-cut: 16px;
|
||
--ng-speed: 120ms;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
background: var(--ng-void);
|
||
color: var(--ng-text);
|
||
font: 500 18px/1.5 var(--ng-font-ui);
|
||
}
|
||
|
||
a { color: var(--ng-cyan); }
|
||
a:hover { color: var(--ng-yellow); }
|
||
h1, h2, h3 { font-weight: 700; text-transform: uppercase; line-height: 1.1; }
|
||
h1 { color: var(--ng-yellow); }
|
||
|
||
/* Mono machine-style label: "CPU LOAD", "01 // COLOR" */
|
||
.ng-label {
|
||
font: 600 12px/1.4 var(--ng-font-data);
|
||
letter-spacing: 0.15em;
|
||
text-transform: uppercase;
|
||
color: var(--ng-dim);
|
||
}
|
||
.ng-data { font-family: var(--ng-font-data); font-variant-numeric: tabular-nums; }
|
||
|
||
/* Panel with one clipped top-right corner */
|
||
.ng-panel {
|
||
background: var(--ng-panel);
|
||
border: 1px solid var(--ng-line);
|
||
padding: 20px 24px;
|
||
clip-path: polygon(0 0, calc(100% - var(--ng-cut)) 0, 100% var(--ng-cut), 100% 100%, 0 100%);
|
||
}
|
||
|
||
/* Buttons: one primary (yellow) per view */
|
||
.ng-btn {
|
||
min-height: 48px;
|
||
padding: 0 28px;
|
||
border: 1px solid var(--ng-line);
|
||
background: transparent;
|
||
color: var(--ng-dim);
|
||
font: 700 18px var(--ng-font-ui);
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
cursor: pointer;
|
||
transition: filter var(--ng-speed) linear;
|
||
}
|
||
.ng-btn:hover { filter: brightness(1.2); }
|
||
.ng-btn:focus-visible { outline: 2px solid var(--ng-yellow); outline-offset: 3px; }
|
||
.ng-btn--primary {
|
||
background: var(--ng-yellow);
|
||
color: var(--ng-void);
|
||
border: 0;
|
||
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
|
||
}
|
||
.ng-btn--info { color: var(--ng-cyan); border-color: var(--ng-cyan); }
|
||
.ng-btn--danger { color: var(--ng-red-txt); border-color: var(--ng-red); }
|
||
|
||
/* Tags */
|
||
.ng-tag {
|
||
display: inline-block;
|
||
padding: 4px 10px;
|
||
border: 1px solid var(--ng-line);
|
||
color: var(--ng-dim);
|
||
font: 600 12px var(--ng-font-data);
|
||
letter-spacing: 0.12em;
|
||
text-transform: uppercase;
|
||
}
|
||
.ng-tag--priority { background: var(--ng-yellow); color: var(--ng-void); border-color: var(--ng-yellow); }
|
||
.ng-tag--live { color: var(--ng-cyan); border-color: var(--ng-cyan); }
|
||
.ng-tag--ok { color: var(--ng-green); border-color: var(--ng-green); }
|
||
.ng-tag--breach { background: var(--ng-red); color: #fff; border-color: var(--ng-red); }
|
||
|
||
/* Segmented meter: <div class="ng-meter"><i class="on"></i>…10×</div> */
|
||
.ng-meter { display: flex; gap: 3px; }
|
||
.ng-meter > i { flex: 1; height: 6px; background: var(--ng-raise); }
|
||
.ng-meter > i.on { background: var(--ng-cyan); }
|
||
.ng-meter--danger > i.on { background: var(--ng-red); }
|
||
|
||
/* Alert banner */
|
||
.ng-alert {
|
||
background: rgb(255 0 60 / 0.08);
|
||
border: 1px solid var(--ng-red);
|
||
padding: 20px 24px;
|
||
}
|
||
.ng-alert .ng-label { color: var(--ng-red-txt); }
|
||
|
||
/* Input: yellow underline on focus */
|
||
.ng-input {
|
||
min-height: 48px;
|
||
padding: 0 16px;
|
||
background: var(--ng-void);
|
||
color: var(--ng-text);
|
||
border: 1px solid var(--ng-line);
|
||
font: 16px var(--ng-font-data);
|
||
}
|
||
.ng-input:focus { outline: none; border-bottom: 2px solid var(--ng-yellow); }
|
||
.ng-input[aria-invalid="true"] { border-bottom: 2px solid var(--ng-red); }
|
||
|
||
/* Glow: the single most important value only */
|
||
.ng-glow { text-shadow: 0 0 12px rgb(252 238 10 / 0.6); }
|
||
|
||
/* --- Diagonal structure --- */
|
||
|
||
/* Multi-notch panel: cut top-left, step in the top edge, cut bottom-right */
|
||
.ng-panel--notched {
|
||
clip-path: polygon(0 16px, 16px 0, 55% 0, calc(55% + 12px) 12px, 100% 12px,
|
||
100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%);
|
||
padding-top: 28px;
|
||
}
|
||
|
||
/* Heading underline: thick bar that steps down diagonally into a hairline */
|
||
.ng-rule { position: relative; padding-bottom: 16px; }
|
||
.ng-rule::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 8px;
|
||
background: var(--ng-rule, var(--ng-yellow));
|
||
clip-path: polygon(0 0, 72px 0, 78px 5px, 100% 5px, 100% 6px, 76px 6px, 70px 8px, 0 8px);
|
||
}
|
||
|
||
/* Stepped edge band along the bottom of a section */
|
||
.ng-edge { position: relative; padding-bottom: 40px; }
|
||
.ng-edge::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
height: 14px;
|
||
background: var(--ng-edge, var(--ng-line));
|
||
clip-path: polygon(0 100%, 0 0, 22% 0, calc(22% + 14px) 100%, 52% 100%, calc(52% + 7px) 50%,
|
||
70% 50%, calc(70% + 7px) 100%, 90% 100%, calc(90% + 14px) 0, 100% 0, 100% 100%);
|
||
}
|
||
.ng-edge--hot { --ng-edge: var(--ng-yellow); }
|
||
|
||
/* --- Glitch --- */
|
||
/* .ng-glitch marks candidates; night-grid.js adds .is-glitching to one at random every 6–14s */
|
||
.ng-glitch.is-glitching { animation: ng-glitch 360ms steps(1, end); }
|
||
@keyframes ng-glitch {
|
||
20% { transform: translateX(-3px) skewX(-12deg); text-shadow: 2px 0 var(--ng-red), -2px 0 var(--ng-cyan); clip-path: inset(10% 0 55% 0); }
|
||
40% { transform: translateX(3px) skewX(8deg); clip-path: inset(60% 0 8% 0); }
|
||
60% { transform: translateX(-1px); text-shadow: -2px 0 var(--ng-red), 2px 0 var(--ng-cyan); clip-path: inset(30% 0 35% 0); }
|
||
80% { transform: none; text-shadow: 1px 0 var(--ng-red), -1px 0 var(--ng-cyan); }
|
||
}
|
||
|
||
/* --- Fold / unfold ---
|
||
<details class="ng-fold" open><summary>Title</summary>…content…</details>
|
||
Corner chip shows » pointing down (unfold) when closed, up (fold) when open. */
|
||
.ng-fold {
|
||
position: relative;
|
||
background: var(--ng-panel);
|
||
border: 1px solid var(--ng-line);
|
||
}
|
||
.ng-fold > summary {
|
||
list-style: none;
|
||
cursor: pointer;
|
||
min-height: 44px;
|
||
padding: 10px 64px 10px 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
font: 700 24px/1.1 var(--ng-font-ui);
|
||
text-transform: uppercase;
|
||
}
|
||
.ng-fold > summary::-webkit-details-marker { display: none; }
|
||
.ng-fold > summary:focus-visible { outline: 2px solid var(--ng-yellow); outline-offset: -2px; }
|
||
.ng-fold > summary::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 44px;
|
||
height: 44px;
|
||
background: var(--ng-yellow);
|
||
clip-path: polygon(0 0, 100% 0, 100% 100%, 12px 100%, 0 calc(100% - 12px));
|
||
}
|
||
.ng-fold > summary::after {
|
||
content: "»";
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 44px;
|
||
height: 44px;
|
||
display: grid;
|
||
place-items: center;
|
||
color: var(--ng-void);
|
||
font: 700 24px/1 var(--ng-font-data);
|
||
transform: rotate(90deg);
|
||
transition: transform var(--ng-speed) steps(2);
|
||
}
|
||
.ng-fold[open] > summary::after { transform: rotate(-90deg); }
|
||
.ng-fold > :not(summary) { margin: 0; padding: 0 24px 20px; }
|
||
|
||
/* Scanline overlay: add class to <body> */
|
||
.ng-scanlines::after {
|
||
content: "";
|
||
position: fixed;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
background: repeating-linear-gradient(0deg, rgb(255 255 255 / 0.025) 0 1px, transparent 1px 3px);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.ng-scanlines::after { display: none; }
|
||
* { transition: none !important; animation: none !important; }
|
||
}
|