Brain: terracotta for fault, a ring set off from the fill, and a dotted ground

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XC2jX6Hdj7pxGGKzBTrbqB
This commit is contained in:
2026-08-17 15:07:30 +02:00
co-authored by Claude Opus 5
parent e19776aed1
commit 59295b1646
3 changed files with 114 additions and 37 deletions
+73 -21
View File
@@ -137,34 +137,71 @@
.react-flow.brain-flat {
--xy-background-color: transparent;
--brain-decay: 90s;
/* Thicker than the editor's, to match the weight the mark draws them at. */
--xy-edge-stroke-width: 2;
--xy-background-pattern-dots-color: color-mix(
in srgb,
var(--muted-foreground) 22%,
transparent
);
}
/*
* A neuron is the node of the brand mark: a disc of the identity blue inside a
* thick ring, wired to its neighbours. The ring thickness is a share of the
* diameter and lives in `BrainNode`, which is where the diameter is known.
* The ground the mark's nodes sit on. The band has no surface of its own and no
* border, so a dot grid that ran to the edges would draw the rectangle the band
* otherwise does not have. An elliptical mask fades it out well before any
* edge — the same shape the layout settles into, so the dots run out roughly
* where the graph does. The stops are the mask's alpha, not a colour: opaque
* keeps the dots, transparent drops them.
*/
.brain-flat .react-flow__background {
mask-image: radial-gradient(
ellipse 62% 62% at 50% 50%,
black 0%,
transparent 100%
);
}
/*
* A neuron is the node of the brand mark: a disc of the identity blue, a gap in
* the surface colour, then a thick ring. Ring and gap are shares of the
* diameter and live in `BrainNode`, which is where the diameter is known; the
* gap is what the mark's tile shows between the two, which here is the card the
* band sits flat on.
*
* The fill is identity and carries no status — that is the ring's job, and the
* word under it. What the fill's lightness carries is how recently this page
* saw the neuron publish: brightest just after it fires, decaying to a floor it
* never drops below, so a graph left open sorts itself into what is busy and
* what is not. The floor stays blue enough to still read as the mark. A fresh
* page has seen nothing either way, so everything starts at `.brain-idle`,
* halfway, claiming no history it does not have.
* The two parts carry the two faults, the way the mark is built: the ring is
* the wiring, so a flow that cannot run as written turns it `--brand-secondary`;
* the disc is the node, so a run that broke turns the fill the same terracotta.
* Both at once still reads, since the gap keeps them apart. This is the one
* place terracotta means fault rather than a call to action — see the root
* DESIGN-GUIDELINES.md → Colour tokens.
*
* What the fill's lightness carries is how recently this page saw the neuron
* publish: brightest just after it fires, decaying to a floor it never drops
* below, so a graph left open sorts itself into what is busy and what is not.
* The floor stays saturated enough to still read as the mark. A fresh page has
* seen nothing either way, so everything starts at `.brain-idle`, halfway,
* claiming no history it does not have.
*/
.brain-cell {
--brain-fill: var(--primary);
--brain-gap: var(--card);
cursor: pointer;
background-color: color-mix(in srgb, var(--primary) 50%, transparent);
background-color: color-mix(in srgb, var(--brain-fill) 50%, transparent);
transition: border-color var(--duration-base) var(--ease-standard);
}
.brain-cell.brain-fault {
--brain-fill: var(--brand-secondary);
}
.brain-cell.brain-idle {
background-color: color-mix(in srgb, var(--primary) 65%, transparent);
background-color: color-mix(in srgb, var(--brain-fill) 65%, transparent);
}
/* Snapped into, decayed out of — never the other way round. */
.brain-cell.brain-hot {
background-color: color-mix(in srgb, var(--primary) 90%, transparent);
background-color: color-mix(in srgb, var(--brain-fill) 90%, transparent);
transition: none;
}
@@ -174,17 +211,27 @@
border-color var(--duration-base) var(--ease-standard),
background-color var(--brain-decay) linear;
}
/*
* A fault is a state change, not a falloff. Sharing the decay's transition
* would have the disc creep from blue to terracotta over a minute and a half,
* so the fill takes the same 200ms the ring beside it does.
*/
.brain-cell.brain-fault {
transition:
border-color var(--duration-base) var(--ease-standard),
background-color var(--duration-base) var(--ease-standard);
}
}
/*
* Outside the ring rather than in it: the ring is what says whether this neuron
* can run, and the disc is already `--primary`, so recolouring either to mark
* the pointer would either overwrite a status or vanish into the fill.
* The pointer lights the gap rather than adding a fourth band around a circle
* that already has three. Ring and disc both carry a status, so neither can be
* recoloured to mark a hover; the surface between them carries nothing.
*/
.brain-cell:hover,
.react-flow__node:focus-visible .brain-cell {
outline: 2px solid var(--ring);
outline-offset: 2px;
--brain-gap: var(--ring);
}
/*
@@ -215,18 +262,23 @@
* The same falloff as the neurons, on the connection instead of the disc. It
* carries the line and its two end dots alike, so the ink is a variable on the
* group they share rather than a stroke on each.
*
* Drawn from `--foreground`, the same token as the rings: in the mark a
* connection is the same stroke as the ring it runs into, and mixing the two
* families instead left the graph looking like rims in one drawing and wires
* from another.
*/
.brain-wire {
--brain-ink: color-mix(in srgb, var(--muted-foreground) 38%, transparent);
--brain-ink: color-mix(in srgb, var(--foreground) 32%, transparent);
}
.brain-wire.brain-idle {
--brain-ink: color-mix(in srgb, var(--muted-foreground) 55%, transparent);
--brain-ink: color-mix(in srgb, var(--foreground) 48%, transparent);
}
/* Snapped into, decayed out of — never the other way round. */
.brain-wire.brain-hot {
--brain-ink: color-mix(in srgb, var(--muted-foreground) 85%, transparent);
--brain-ink: color-mix(in srgb, var(--foreground) 80%, transparent);
}
.react-flow__edge-path.brain-edge {