mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 08:04:25 -10:00
wip(web): foundation changes left by the stopped website build
Preserved for the restarted website build (whale-road direction). Not verified. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01R9rJEoMuUSRWznU6QjkE7h
This commit is contained in:
1 parent
58b1dd3dd3
commit
daf8423432
7 files changed
+591
-765
No files matched your search
@@ -102,7 +102,7 @@ def gpui_tokens(data):
|
||||
"BG": "background", "TEXT": "foreground", "PANEL": "surface",
|
||||
"TEXT_MUTED": "muted_foreground", "BORDER": "border", "SIDEBAR": "sidebar",
|
||||
"PRIMARY": "primary", "ON_PRIMARY": "primary_foreground", "ACCENT": "hover",
|
||||
"LIST_ACTIVE": "selected",
|
||||
"LIST_ACTIVE": "selected", "ATTENTION": "attention",
|
||||
}
|
||||
return [
|
||||
(f"{prefix}_{name}", tuple(bytes.fromhex(palette[key])))
|
||||
|
||||
+37
-74
@@ -7,10 +7,10 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
/* The sheet: one opaque paper field. The descent into the water is drawn
|
||||
by the waterline band and the ocean column, never by a page gradient. */
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
/* The twilight floor: one opaque field in the page's appearance. The one
|
||||
descent into the stage is a hard horizon, never a page gradient. */
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: var(--font-body);
|
||||
font-feature-settings: "ss01", "cv11", "tnum";
|
||||
max-width: 100%;
|
||||
@@ -27,26 +27,8 @@ main, header, footer, nav { position: relative; z-index: 1; }
|
||||
with a little negative tracking on the big headings. Code uses the system
|
||||
monospace stack. No serif, no all-caps, no wide tracking. */
|
||||
.font-display { font-family: var(--font-display); font-weight: 600; }
|
||||
.font-condensed { font-family: var(--font-display); font-weight: 600; }
|
||||
.font-cjk { font-family: var(--font-cjk); }
|
||||
|
||||
/* CJK paragraph rhythm — looser leading, wider tracking for body; tighter for headings */
|
||||
.cjk-body {
|
||||
line-height: 1.9;
|
||||
letter-spacing: 0.02em;
|
||||
word-break: break-all;
|
||||
}
|
||||
.cjk-heading {
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.cjk-prose p {
|
||||
line-height: 1.9;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
/* Full-width punctuation should use CJK spacing */
|
||||
.cjk-prose {
|
||||
font-feature-settings: "halt", "pwid";
|
||||
}
|
||||
.font-mono { font-family: var(--font-mono); }
|
||||
|
||||
/* ---------- the ` · ` chain ---------- */
|
||||
@@ -166,16 +148,6 @@ html[lang="ko"] h3 {
|
||||
padding-block: var(--section-pad);
|
||||
}
|
||||
|
||||
.double-rule {
|
||||
background-image:
|
||||
linear-gradient(var(--hairline), var(--hairline)),
|
||||
linear-gradient(var(--hairline), var(--hairline));
|
||||
background-size: 100% 1px, 100% 1px;
|
||||
background-position: top, bottom;
|
||||
background-repeat: no-repeat;
|
||||
padding: 0.45rem 0;
|
||||
}
|
||||
|
||||
.col-rule > * + * {
|
||||
border-left: 1px solid var(--hairline);
|
||||
}
|
||||
@@ -244,73 +216,64 @@ html[lang="ko"] h3 {
|
||||
|
||||
/* ---------- numbers ---------- */
|
||||
.tabular { font-variant-numeric: tabular-nums; }
|
||||
.bignum {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: 2.2rem;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ---------- code blocks ---------- */
|
||||
/* Code is set into the floor: the recessed surface, no border, the page's
|
||||
own ink. Prompts and comments recede to muted; keys stay in the text ink
|
||||
at 500. Nothing here is decorative colour. */
|
||||
pre.code-block {
|
||||
background: var(--code-bg);
|
||||
color: var(--code-fg);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
padding: 1rem 1.1rem;
|
||||
margin: 0;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-mono);
|
||||
line-height: 1.55;
|
||||
border: 1px solid var(--stage-line);
|
||||
border: 0;
|
||||
border-radius: var(--radius-surface);
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
white-space: pre;
|
||||
tab-size: 2;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
pre.code-block::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 1px;
|
||||
background: var(--hairline);
|
||||
}
|
||||
pre.code-block .prompt { color: var(--action-on-dark); }
|
||||
pre.code-block .comment { color: var(--gpui-stage-ink-dim); }
|
||||
pre.code-block .key { color: var(--signal-gold); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
pre.code-block { padding: 0.85rem 0.95rem; }
|
||||
}
|
||||
pre.code-block .prompt,
|
||||
pre.code-block .comment { color: var(--muted); }
|
||||
pre.code-block .key { color: var(--text); font-weight: 500; }
|
||||
|
||||
code.inline {
|
||||
background: var(--paper-deep);
|
||||
border: 1px solid var(--hairline);
|
||||
padding: 0.05rem 0.32rem;
|
||||
background: var(--surface);
|
||||
padding: 0.0625rem 0.3125rem;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.85em;
|
||||
font-size: 0.875em;
|
||||
border-radius: var(--radius-control);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- search ---------- */
|
||||
/* The legacy search input takes the search-field recipe: a recessed
|
||||
field whose edge identifies it (--line-strong, 3:1), ringed on focus. No
|
||||
hover colour: only focus goes blue. */
|
||||
.search-input {
|
||||
width: 100%;
|
||||
min-height: 2.5rem;
|
||||
font-family: var(--font-body);
|
||||
font-size: 1rem;
|
||||
padding: 0.75rem 2.5rem 0.75rem 1rem;
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
border: 1px solid var(--hairline);
|
||||
transition: border-color var(--dur-state) var(--ease-spring);
|
||||
font-size: var(--text-body);
|
||||
padding-block: var(--space-2);
|
||||
padding-inline: var(--space-3) var(--space-6);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: var(--radius-control);
|
||||
transition: border-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
.search-input::placeholder { color: var(--ink-mute); }
|
||||
.search-input:focus { border-color: var(--ring); }
|
||||
.search-input::placeholder { color: var(--muted); }
|
||||
.search-input:focus-visible { border-color: var(--ring); }
|
||||
|
||||
mark.search-highlight {
|
||||
background: var(--indigo-pale);
|
||||
color: var(--indigo-deep);
|
||||
padding: 0.02em 0.08em;
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--selection);
|
||||
color: inherit;
|
||||
}
|
||||
+167
-205
@@ -1,11 +1,16 @@
|
||||
/* ---------- nav link ---------- */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Site chrome — the header, the mobile sheet, the footer stage */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* ---------- header ---------- */
|
||||
/* An opaque bar on the reading floor with one hairline under it. No glass,
|
||||
no blur: the page scrolls beneath a solid surface. */
|
||||
.site-nav {
|
||||
position: sticky;
|
||||
z-index: 30;
|
||||
top: 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
background: rgb(var(--c-paper) / 0.94);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.site-nav-inner {
|
||||
@@ -22,17 +27,18 @@
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
color: var(--ink);
|
||||
gap: var(--space-2);
|
||||
color: var(--mark-ink);
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.15rem;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.03em;
|
||||
border-radius: var(--radius-control);
|
||||
}
|
||||
|
||||
.paper-nav-inner {
|
||||
min-height: 3.85rem;
|
||||
padding-block: 0.55rem;
|
||||
min-height: 3.75rem;
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
|
||||
.paper-wordmark {
|
||||
@@ -51,6 +57,8 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* The mark and the wordmark are one ink, drawn in currentColor, so both
|
||||
appearances re-ink them without a filter or a second file. */
|
||||
.paper-wordmark-mark {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
@@ -58,8 +66,6 @@
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
/* The traced wordmark is ~7.1:1; when the compact nav clamps its width the
|
||||
glyphs scale down inside the box instead of squashing. */
|
||||
.paper-wordmark-logo {
|
||||
display: block;
|
||||
flex: 0 1 auto;
|
||||
@@ -67,39 +73,27 @@
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
height: 20px;
|
||||
object-fit: contain;
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
/* The traced wordmark is navy ink (#142352), which disappears on the dark
|
||||
scheme's charcoal. Under dark (OS or pinned) it is re-inked to the light
|
||||
stage foreground; the footer already uses the reversed cut. */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) .paper-wordmark-logo {
|
||||
filter: brightness(0) invert(0.93);
|
||||
}
|
||||
}
|
||||
:root[data-theme="dark"] .paper-wordmark-logo {
|
||||
filter: brightness(0) invert(0.93);
|
||||
}
|
||||
|
||||
/* The one primary action in the header. It keeps its class for layout;
|
||||
its paint is the .btn-primary recipe. */
|
||||
.paper-install-cta {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 2.25rem;
|
||||
padding: 0.35rem 0.85rem;
|
||||
background: var(--indigo);
|
||||
color: var(--paper);
|
||||
padding-inline: var(--space-3);
|
||||
background: var(--accent);
|
||||
color: var(--on-accent);
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-control);
|
||||
transition: background-color var(--dur-state) var(--ease-spring);
|
||||
transition: background-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
|
||||
.paper-install-cta:hover {
|
||||
background: var(--indigo-deep);
|
||||
color: var(--paper);
|
||||
color: var(--on-accent);
|
||||
}
|
||||
|
||||
/* The one identity door: a quiet text control beside the primary action. */
|
||||
@@ -107,13 +101,13 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 2.25rem;
|
||||
padding: 0.35rem 0.75rem;
|
||||
color: var(--ink);
|
||||
padding-inline: var(--space-3);
|
||||
color: var(--text);
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-control);
|
||||
transition: background-color var(--dur-state) var(--ease-spring);
|
||||
transition: background-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
|
||||
.paper-auth-signin:hover { background: var(--hover); }
|
||||
@@ -128,12 +122,12 @@
|
||||
}
|
||||
|
||||
.site-nav-actions > * { flex-shrink: 0; }
|
||||
.site-nav-actions > .paper-auth-signin { margin-left: 0.5rem; }
|
||||
.site-nav-actions > .paper-install-cta { margin-left: 0.25rem; }
|
||||
.site-nav-actions > .paper-auth-signin { margin-inline-start: 0.5rem; }
|
||||
.site-nav-actions > .paper-install-cta { margin-inline-start: 0.25rem; }
|
||||
|
||||
/* Quiet icon controls (theme, language, GitHub, menu): a 40px target around
|
||||
a 20px glyph, the GPUI rail metrics. Muted at rest, full ink on hover and
|
||||
while the control is expanded; the accessible name carries the state. */
|
||||
/* Quiet icon controls: a 40px target around a 20px glyph, the GPUI rail
|
||||
metrics. Muted at rest, full ink on hover and while expanded; a pressed
|
||||
or current control takes the neutral selected fill. */
|
||||
.nav-icon-button {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
@@ -141,16 +135,38 @@
|
||||
height: 2.5rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--ink-soft);
|
||||
color: var(--muted);
|
||||
border-radius: var(--radius-control);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--dur-state) var(--ease-spring), color var(--dur-state) var(--ease-spring);
|
||||
transition-property: background-color, color, box-shadow;
|
||||
transition-duration: var(--dur-state);
|
||||
transition-timing-function: var(--ease-out);
|
||||
}
|
||||
|
||||
.nav-icon-button:hover,
|
||||
.nav-icon-button[aria-expanded="true"] {
|
||||
background: var(--hover);
|
||||
color: var(--ink);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-icon-button[aria-pressed="true"],
|
||||
.nav-icon-button[aria-current="page"] {
|
||||
background: var(--selected);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-icon-button:active { opacity: 0.75; }
|
||||
.nav-icon-button:focus-visible { box-shadow: var(--glow-focus); }
|
||||
|
||||
/* The small variant for controls inside rows and code blocks; it grows to
|
||||
the touch floor on coarse pointers. */
|
||||
.nav-icon-button-sm {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
.nav-icon-button-sm .nav-icon { width: 1rem; height: 1rem; }
|
||||
@media (pointer: coarse) {
|
||||
.nav-icon-button-sm { width: 2.75rem; height: 2.75rem; }
|
||||
}
|
||||
|
||||
.nav-icon { display: block; width: 1.25rem; height: 1.25rem; }
|
||||
@@ -169,12 +185,11 @@
|
||||
}
|
||||
|
||||
.nav-locale:has(select:focus-visible) {
|
||||
box-shadow: 0 0 0 var(--gpui-focus-width) var(--ring);
|
||||
box-shadow: 0 0 0 var(--gpui-focus-width) var(--ring), var(--glow-focus);
|
||||
}
|
||||
|
||||
/* Compact sheet rows: one label per destination, a chevron, a selected fill
|
||||
for the current page rather than a decorative bar. */
|
||||
.mm-list > li + li { border-top: 1px solid var(--hairline); }
|
||||
/* ---------- the mobile sheet ---------- */
|
||||
.mm-list > li + li { border-top: 1px solid var(--line); }
|
||||
|
||||
.mm-link {
|
||||
display: flex;
|
||||
@@ -182,15 +197,15 @@
|
||||
gap: 0.75rem;
|
||||
min-height: 3.25rem;
|
||||
padding-inline: 0.5rem;
|
||||
color: var(--ink);
|
||||
color: var(--text);
|
||||
font-size: 1.0625rem;
|
||||
border-radius: var(--radius-control);
|
||||
transition: background-color var(--dur-state) var(--ease-spring);
|
||||
transition: background-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
|
||||
.mm-link:hover { background: var(--hover); }
|
||||
.mm-link[aria-current="page"] { background: var(--selected); }
|
||||
.mm-link-chevron { margin-left: auto; color: var(--ink-mute); }
|
||||
.mm-link-chevron { margin-inline-start: auto; color: var(--muted); }
|
||||
|
||||
.mm-action {
|
||||
display: flex;
|
||||
@@ -201,51 +216,63 @@
|
||||
|
||||
.mm-action.paper-install-cta { margin-top: 1.5rem; }
|
||||
|
||||
/* ---------- primary nav links ---------- */
|
||||
/* Hover is a neutral wash. The current page keeps the 2px accent bar under
|
||||
its label: the active-tab grammar, the one place blue marks "here". */
|
||||
.nav-link {
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.78rem;
|
||||
font-size: 0.8125rem;
|
||||
letter-spacing: 0;
|
||||
color: var(--ink);
|
||||
color: var(--text);
|
||||
position: relative;
|
||||
padding: 0.25rem 0;
|
||||
padding: 0.375rem 0.5rem;
|
||||
margin-inline: -0.5rem;
|
||||
border-radius: var(--radius-control);
|
||||
transition: background-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
.nav-link:hover { background: var(--hover); }
|
||||
.nav-link::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0; right: 0; bottom: -2px;
|
||||
inset-inline: 0.5rem;
|
||||
bottom: -2px;
|
||||
height: 2px;
|
||||
background: var(--indigo);
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform var(--dur-state) var(--ease-spring);
|
||||
opacity: 0;
|
||||
}
|
||||
.nav-link:hover::after, .nav-link[aria-current="page"]::after { transform: scaleX(1); }
|
||||
.nav-link[aria-current="page"]::after { opacity: 1; }
|
||||
|
||||
/* ---------- footer ---------- */
|
||||
/* Compact viewports: the strip keeps the home control clickable. These
|
||||
rules also exist in home.css until the home slice deletes them there. */
|
||||
@media (max-width: 520px) {
|
||||
.site-nav-inner {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.site-nav-actions {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.paper-wordmark {
|
||||
max-width: 9.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- footer — the stage below the horizon ---------- */
|
||||
/* The footer is midnight in both appearances (tokens-roles.css). Its top
|
||||
edge is the page's one horizon: two flat grounds meeting at a pixel, plus
|
||||
a hairline only where they are too close to tell apart (dark). */
|
||||
.site-footer {
|
||||
position: relative;
|
||||
background: var(--ocean-deep);
|
||||
color: var(--stage-soft);
|
||||
border-top: 1px solid var(--horizon-edge);
|
||||
background: var(--bg);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* The waterline before the seabed. Every page that is still on paper when
|
||||
it reaches the footer descends here; the homepage is already under water
|
||||
by then, so its column hides the band and the seabed simply continues. */
|
||||
.site-footer-waterline {
|
||||
position: relative;
|
||||
height: clamp(9rem, 18vw, 15rem);
|
||||
overflow: hidden;
|
||||
background: var(--paper);
|
||||
}
|
||||
.site-footer-waterline > svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
main:has(.ocean-column) + .site-footer .site-footer-waterline { display: none; }
|
||||
.site-footer-main { border-top: 1px solid rgb(var(--c-stage-soft) / 0.13); }
|
||||
main:has(.ocean-column) + .site-footer .site-footer-main { border-top-color: transparent; }
|
||||
/* A page whose main already ends on the stage continues into the footer
|
||||
with no second edge. */
|
||||
main:has(> .midnight:last-child) + .site-footer,
|
||||
main:has(> .ocean-column) + .site-footer { border-top-color: transparent; }
|
||||
|
||||
.site-footer-main,
|
||||
.site-footer-meta {
|
||||
@@ -255,151 +282,86 @@ main:has(.ocean-column) + .site-footer .site-footer-main { border-top-color: tra
|
||||
|
||||
.site-footer-main {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(16rem, 1.25fr) minmax(18rem, 0.75fr);
|
||||
gap: 4rem;
|
||||
padding-block: 2.8rem;
|
||||
grid-template-columns: minmax(14rem, 1fr) minmax(0, 2fr);
|
||||
gap: var(--space-7);
|
||||
padding-block: var(--space-7) var(--space-6);
|
||||
}
|
||||
|
||||
.site-wordmark-footer { color: var(--stage-text); }
|
||||
.site-wordmark-footer img { display: block; width: auto; height: 18px; }
|
||||
.site-footer-brand p { max-width: 30rem; margin-top: 0.8rem; color: rgb(var(--c-stage-soft) / 0.62); font-size: 0.86rem; line-height: 1.6; }
|
||||
.site-footer-brand { display: grid; gap: var(--space-3); align-content: start; }
|
||||
.site-footer-brand p {
|
||||
max-width: 28rem;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-ui);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.site-wordmark-footer { color: var(--text); justify-self: start; }
|
||||
|
||||
.site-footer-links {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 2rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.site-footer-links > div { display: flex; flex-direction: column; gap: 0.55rem; }
|
||||
.site-footer-links span { margin-bottom: 0.25rem; color: var(--ocean-current); font-family: var(--font-body); font-size: 0.75rem; }
|
||||
.site-footer-links a { color: rgb(var(--c-stage-soft) / 0.76); font-size: 0.82rem; }
|
||||
.site-footer-links a:hover { color: var(--stage-text); }
|
||||
.site-footer-group { display: grid; gap: var(--space-2); align-content: start; }
|
||||
|
||||
.site-footer-label {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-caption);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.site-footer-list { display: grid; gap: 2px; }
|
||||
|
||||
.site-footer-list a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 1.75rem;
|
||||
padding-inline: var(--space-2);
|
||||
margin-inline: calc(-1 * var(--space-2));
|
||||
color: var(--text);
|
||||
font-size: var(--text-ui);
|
||||
border-radius: var(--radius-control);
|
||||
transition: background-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
|
||||
.site-footer-list a:hover { background: var(--hover); }
|
||||
|
||||
.site-footer-meta {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
gap: 1.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding-block: 1rem;
|
||||
border-top: 1px solid rgb(var(--c-stage-soft) / 0.13);
|
||||
color: rgb(var(--c-stage-soft) / 0.5);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3) var(--space-5);
|
||||
padding-block: var(--space-4) var(--space-6);
|
||||
border-top: 1px solid var(--line);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.site-footer-meta a:hover { color: var(--ocean-current); }
|
||||
.site-footer-meta > div { display: flex; gap: 1rem; }
|
||||
.site-footer-meta > p { min-width: 0; max-width: 70ch; overflow-wrap: anywhere; }
|
||||
.site-footer-meta p { min-width: 0; max-width: 70ch; overflow-wrap: anywhere; }
|
||||
.site-footer-meta a {
|
||||
color: var(--text);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--line-strong);
|
||||
text-underline-offset: 0.2em;
|
||||
}
|
||||
.site-footer-meta a:hover { text-decoration-color: currentColor; }
|
||||
|
||||
/* ---------- ticker ---------- */
|
||||
.ticker-viewport {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
.site-footer-meta-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2) var(--space-4);
|
||||
}
|
||||
|
||||
.ticker-track {
|
||||
display: inline-flex;
|
||||
gap: 3rem;
|
||||
white-space: nowrap;
|
||||
animation: ticker 80s linear infinite;
|
||||
padding-right: 3rem;
|
||||
}
|
||||
@keyframes ticker {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-50%); }
|
||||
@media (max-width: 900px) {
|
||||
.site-footer-main { grid-template-columns: 1fr; gap: var(--space-6); }
|
||||
}
|
||||
|
||||
/* One wire entry: verb, number or tag, title, by-line, age. Baseline-aligned
|
||||
so the mono verb sits on the same line as the title it qualifies. */
|
||||
.ticker-item {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5rem;
|
||||
@media (forced-colors: active) {
|
||||
.site-footer { border-top-color: CanvasText; }
|
||||
}
|
||||
|
||||
/* The verb carries the state. Merges are the headline case, so they get the
|
||||
one warm-neutral ink on the sheet; releases take ochre; anything closed
|
||||
recedes to mute. Everything else is the action blue. */
|
||||
.ticker-verb {
|
||||
color: var(--indigo);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.ticker-verb[data-event="merged"] { color: var(--jade); }
|
||||
.ticker-verb[data-event="published"] { color: var(--ochre); }
|
||||
.ticker-verb[data-event="closed"] { color: var(--ink-mute); }
|
||||
|
||||
.ticker-num,
|
||||
.ticker-tag,
|
||||
.ticker-by,
|
||||
.ticker-age {
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.ticker-title {
|
||||
color: var(--ink);
|
||||
max-width: 24rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* The contributor's handle is the point of the line, not a footnote. */
|
||||
.ticker-handle {
|
||||
color: var(--ink);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ticker-first {
|
||||
padding: 0 0.35rem;
|
||||
border: 1px solid var(--ochre);
|
||||
color: var(--ochre);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.ticker-sep { color: var(--paper-line-soft); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ticker-track { animation: none; }
|
||||
/* A frozen track would clip every entry past the fold with no way to reach
|
||||
them. Stopping the motion must not also remove the content. */
|
||||
.ticker-viewport { overflow-x: auto; }
|
||||
}
|
||||
|
||||
/* ---------- ambient ---------- */
|
||||
/*
|
||||
* One effect, OPT-IN under `no-preference` rather than opted out under
|
||||
* `reduce`. A `reduce` override can be defeated by specificity; a gate cannot.
|
||||
* Reduced motion freezes the field — it never removes it, so the column's
|
||||
* gradient and the whale below are unconditional and only the movement is not.
|
||||
* The whale mark itself is always the still pose.
|
||||
*
|
||||
* The TUI's ambient life — fish, jellyfish, bubbles, the whale cameo — stays
|
||||
* out of scope. This page quotes the product's restraint doctrine in its own
|
||||
* copy. The standing loops on the whole site are three — ticker, caret, and
|
||||
* this one. Entrances are transitions, and the one-shot settles live in the
|
||||
* Motion section below under the same no-preference gate.
|
||||
*/
|
||||
|
||||
/* M1 — the column breath. Opacity only, on a 90s cycle: the TUI's authored
|
||||
0.018–0.055 phase bias expressed as the same fraction of a mix. Opacity is
|
||||
the one property that cannot cause layout work on an element four bands
|
||||
tall. No position change, no hue shift, no filter. */
|
||||
@keyframes cw-breath {
|
||||
0%, 100% { opacity: 0; }
|
||||
50% { opacity: 0.045; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.ocean-column::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
background: var(--stage-ambient);
|
||||
opacity: 0;
|
||||
animation: cw-breath 90s ease-in-out infinite;
|
||||
}
|
||||
@media print {
|
||||
.site-footer { border-top-color: currentColor; background: none; color: inherit; }
|
||||
}
|
||||
+50
-136
@@ -1,187 +1,101 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Shared surface states — empty / loading / error (surface-state.tsx) */
|
||||
/* Route boundaries and the connection banner */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/*
|
||||
* One plate for every data-bearing page's non-data moments. The mark on the
|
||||
* left carries the state in colour AND the copy carries it in words, so no
|
||||
* state is conveyed by colour alone: mute for empty, action blue for loading,
|
||||
* coral for error — the status-bar grammar the product already speaks.
|
||||
* The empty / loading / error grammar itself lives in primitives.css
|
||||
* (.empty-state, .skeleton-bar); components/surface-state.tsx renders it.
|
||||
* This file keeps the two containers around it.
|
||||
*/
|
||||
.state-block {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
padding: clamp(1.5rem, 4vw, 2.25rem) clamp(1.25rem, 3vw, 1.75rem);
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--paper-card);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.state-block-compact {
|
||||
padding: 1rem 1.15rem;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.state-mark {
|
||||
width: 0.6rem;
|
||||
height: 0.6rem;
|
||||
margin-top: 0.42rem;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--ink-mute);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.state-block-loading .state-mark { background: var(--indigo); }
|
||||
.state-block-error .state-mark { background: var(--ocean-coral); }
|
||||
.state-block-error { border-color: color-mix(in srgb, var(--ocean-coral) 45%, var(--stage-line)); }
|
||||
|
||||
.state-copy { min-width: 0; }
|
||||
|
||||
.state-title {
|
||||
color: var(--ink);
|
||||
font-size: 0.98rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.state-body {
|
||||
max-width: 44rem;
|
||||
margin-top: 0.4rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.state-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.state-retry[aria-busy="true"] { opacity: 0.7; cursor: progress; }
|
||||
|
||||
/* Skeleton lines: neutral bars, never fake words. The shimmer is opt-in
|
||||
under no-preference; reduced motion sees still bars. */
|
||||
.state-skeleton {
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
|
||||
.state-skeleton > span {
|
||||
display: block;
|
||||
height: 0.7rem;
|
||||
border-radius: var(--radius-control);
|
||||
background: color-mix(in srgb, var(--ink-mute) 22%, transparent);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.state-skeleton > span {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
color-mix(in srgb, var(--ink-mute) 18%, transparent) 0%,
|
||||
color-mix(in srgb, var(--ink-mute) 34%, transparent) 50%,
|
||||
color-mix(in srgb, var(--ink-mute) 18%, transparent) 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: state-shimmer 1.6s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes state-shimmer {
|
||||
from { background-position: 200% 0; }
|
||||
to { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* Route-level boundaries (loading.tsx / error.tsx / not-found.tsx) sit in
|
||||
the site container with the page rhythm so a boundary never looks like a
|
||||
the site container with the page rhythm, so a boundary never looks like a
|
||||
different site. */
|
||||
.route-state {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
padding-block: clamp(3rem, 6vw, 4.75rem);
|
||||
padding-block: var(--space-6) var(--space-8);
|
||||
max-width: 52rem;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Connection banner — offline / reconnecting / degraded / restored */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* A raised sheet with a dot and a word. The dot's hue is the state
|
||||
vocabulary (danger offline, accent reconnecting, attention degraded, live
|
||||
restored) and the title always says it in words. No blur, no pulse. */
|
||||
.connection-banner {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 35;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 0.9rem;
|
||||
gap: var(--space-3);
|
||||
align-items: center;
|
||||
width: var(--container);
|
||||
margin: 0.75rem auto 0;
|
||||
padding: 0.7rem 1rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: var(--radius-control);
|
||||
background: color-mix(in srgb, var(--paper-card) 92%, transparent);
|
||||
backdrop-filter: blur(8px);
|
||||
color: var(--ink);
|
||||
margin: var(--space-3) auto 0;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-surface);
|
||||
background: var(--panel);
|
||||
box-shadow: var(--shadow-popover);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.connection-mark {
|
||||
width: 0.6rem;
|
||||
height: 0.6rem;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--ink-mute);
|
||||
background: currentColor;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.connection-banner-offline { border-color: color-mix(in srgb, var(--ocean-coral) 50%, var(--stage-line)); }
|
||||
.connection-banner-offline .connection-mark { background: var(--ocean-coral); }
|
||||
.connection-banner-reconnecting .connection-mark { background: var(--indigo); }
|
||||
.connection-banner-degraded .connection-mark { background: var(--ochre); }
|
||||
.connection-banner-restored { border-color: color-mix(in srgb, var(--jade) 50%, var(--stage-line)); }
|
||||
.connection-banner-restored .connection-mark { background: var(--jade); }
|
||||
.connection-banner-offline .connection-mark { color: var(--danger); }
|
||||
.connection-banner-reconnecting .connection-mark { color: var(--accent); box-shadow: var(--glow); }
|
||||
.connection-banner-degraded .connection-mark { color: var(--attention); box-shadow: var(--glow); }
|
||||
.connection-banner-restored .connection-mark { color: var(--live); }
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.connection-banner-reconnecting .connection-mark {
|
||||
animation: connection-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
.connection-banner {
|
||||
animation: mm-in var(--dur-spring) var(--ease-spring) both;
|
||||
animation: mm-in var(--dur-arrive) var(--ease-out) both;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes connection-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.35; }
|
||||
}
|
||||
|
||||
.connection-copy { min-width: 0; }
|
||||
.connection-title { font-size: 0.86rem; font-weight: 600; }
|
||||
.connection-body { margin-top: 0.15rem; color: var(--ink-soft); font-size: 0.78rem; line-height: 1.5; }
|
||||
.connection-checked { color: var(--ink-mute); font-family: var(--font-mono); font-size: 0.75rem; }
|
||||
.connection-title { font-size: var(--text-ui); font-weight: 500; }
|
||||
.connection-body { margin-top: 0.125rem; color: var(--muted); font-size: var(--text-caption); line-height: 1.5; }
|
||||
.connection-checked { color: var(--muted); }
|
||||
|
||||
.connection-actions { display: flex; gap: 0.5rem; }
|
||||
.connection-actions { display: flex; gap: var(--space-2); }
|
||||
|
||||
/* The banner's buttons take the .btn secondary and primary paint under
|
||||
the class names the component emits. */
|
||||
.connection-button {
|
||||
display: inline-flex;
|
||||
min-height: 2.25rem;
|
||||
min-height: 2rem;
|
||||
align-items: center;
|
||||
padding: 0.35rem 0.75rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
padding-inline: var(--space-3);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-control);
|
||||
color: var(--ink);
|
||||
background: var(--panel);
|
||||
color: var(--text);
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
transition: border-color var(--dur-state) var(--ease-spring), background-color var(--dur-state) var(--ease-spring), color var(--dur-state) var(--ease-spring);
|
||||
font-size: var(--text-ui);
|
||||
font-weight: 500;
|
||||
transition-property: background-color, border-color, color;
|
||||
transition-duration: var(--dur-state);
|
||||
transition-timing-function: var(--ease-out);
|
||||
}
|
||||
|
||||
.connection-button:hover { border-color: var(--indigo); color: var(--indigo); }
|
||||
.connection-button-primary { border-color: var(--indigo); background: var(--indigo); color: var(--paper); }
|
||||
.connection-button-primary:hover { background: var(--indigo-deep); border-color: var(--indigo-deep); color: var(--paper); }
|
||||
.connection-button:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.connection-button:hover { background: var(--hover); }
|
||||
.connection-button-primary { border-color: transparent; background: var(--accent); color: var(--on-accent); }
|
||||
.connection-button-primary:hover { background: var(--accent-hover); color: var(--on-accent); }
|
||||
.connection-button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.connection-button[aria-busy="true"] { cursor: progress; }
|
||||
|
||||
@media (pointer: coarse) {
|
||||
.connection-button { min-height: 2.75rem; }
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.connection-banner {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
|
||||
+298
-183
@@ -1,58 +1,193 @@
|
||||
/* ---------- site tokens — the folio ---------- */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Tokens and roles — the whale-road */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/*
|
||||
* The site follows the OS appearance, exactly as the GPUI client follows its
|
||||
* `set_theme` pair: the GPUI light theme (warm paper) by default, the GPUI
|
||||
* dark theme (warm charcoal) under `prefers-color-scheme: dark` unless the
|
||||
* page pins `data-theme="light"`, and dark whenever it pins
|
||||
* `data-theme="dark"`. The pin comes from the theme toggle (every page), and
|
||||
* the inline boot script in the locale layout applies it before paint.
|
||||
* One token authority: vendor/codewhale-design/tokens.json, generated into
|
||||
* ../tokens.css as `--gpui-light-*` / `--gpui-dark-*`. This file names the
|
||||
* roles every surface paints with (codewhale-design/DIRECTION.md):
|
||||
*
|
||||
* The homepage water column (`.ocean-column`) and the footer seabed
|
||||
* (`.site-footer`) are the stage: a fixed dark material in both schemes, like
|
||||
* a terminal plate, because the waterline strata that descend into them are
|
||||
* drawn in the stage's own tokens. They share the one dark block below.
|
||||
* Depth is elevation.
|
||||
* sunlit --panel raised plates, popovers, the thing in focus
|
||||
* twilight --bg the reading floor
|
||||
* recessed --surface code, rails, chrome set into the floor
|
||||
* midnight .midnight the stage below the one horizon (the footer,
|
||||
* a home descent); always the dark set, in both
|
||||
* appearances
|
||||
*
|
||||
* The token NAMES keep their historical shape (`--paper`, `--ink`,
|
||||
* `--indigo`) so every component rule stays diffable; the VALUES resolve
|
||||
* through the `--gpui-*` aliases below, which reference the generated GPUI
|
||||
* tokens from vendor/codewhale-design/tokens.json. Only the warm state hues (tan, moss, rust and their AA-adapted
|
||||
* inks) stay literal: `set_theme` has no slot for them.
|
||||
* Bioluminescence is how things light up. Focus, live work and "needs
|
||||
* you" carry a soft halo (--glow, --glow-focus): a point of light in the
|
||||
* dark, quieter on paper. Nothing else glows.
|
||||
*
|
||||
* State is four hues and always a word: --accent (action, focus),
|
||||
* --live (working, done, verified), --attention (needs you, warning),
|
||||
* --danger (failed, destructive). Idle and offline use --muted.
|
||||
*
|
||||
* NAMING TRAP: tokens.json `surface` is the RAISED colour (web --panel);
|
||||
* tokens.json `sidebar` is the RECESSED one (web --surface).
|
||||
*
|
||||
* Every role is declared in each scheme block — light (:root), OS-dark and
|
||||
* pinned-dark (identical), and midnight — because a custom property that
|
||||
* references another is computed where it is declared.
|
||||
*
|
||||
* The legacy names (--paper, --ink, --indigo, --stage-*, --ocean-*, ...)
|
||||
* stay only so pages that have not migrated keep painting; new work uses the
|
||||
* roles. They are removed in the post-renovation sweep.
|
||||
*/
|
||||
|
||||
/* ---------- scheme-independent tokens ---------- */
|
||||
:root {
|
||||
/* --- GPUI light (above the waterline) --- */
|
||||
--gpui-paper: var(--gpui-light-bg); /* background — warm paper */
|
||||
--gpui-paper-raised: var(--gpui-light-panel); /* muted — card/popover */
|
||||
--gpui-paper-muted: var(--gpui-light-sidebar); /* sidebar — recessed */
|
||||
--gpui-ink: var(--gpui-light-text); /* foreground */
|
||||
--gpui-ink-soft: var(--gpui-light-text-muted); /* muted_foreground */
|
||||
--gpui-ink-mute: var(--gpui-light-text-muted); /* muted_foreground */
|
||||
--gpui-edge: var(--gpui-light-border); /* border */
|
||||
--gpui-primary: var(--gpui-light-primary); /* primary */
|
||||
--gpui-primary-hover: rgb(var(--gpui-light-primary-rgb) / var(--gpui-primary-hover-opacity)); /* button_primary_hover */
|
||||
/* State and control-edge hues that tokens.json 1.0.0 has no slot for.
|
||||
Values and their contrast on all five grounds are measured in
|
||||
codewhale-design/reference/MEASUREMENTS.md §2.2–2.3 (proposed
|
||||
tokens.json 1.1.0). Light live #3a6352 ≥ 4.97:1, light danger #9e3f36
|
||||
≥ 4.77:1, dark live #9ec7b2 ≥ 6.21:1, dark danger #e39a90 ≥ 5.12:1 on
|
||||
background, panel, sidebar, hover and selected. border-strong clears
|
||||
the 3:1 non-text floor on the same five grounds. */
|
||||
--gpui-light-live: #3a6352;
|
||||
--gpui-dark-live: #9ec7b2;
|
||||
--gpui-light-danger: #9e3f36;
|
||||
--gpui-dark-danger: #e39a90;
|
||||
--gpui-light-border-strong: #807c76;
|
||||
--gpui-dark-border-strong: #828386;
|
||||
|
||||
/* --- GPUI dark (below the waterline) --- */
|
||||
--gpui-stage: var(--gpui-dark-bg); /* background — warm charcoal */
|
||||
--gpui-stage-deep: var(--gpui-dark-sidebar); /* sidebar — deepest dark */
|
||||
--gpui-stage-muted: var(--gpui-dark-panel); /* muted — raised input plate */
|
||||
--gpui-stage-raised: var(--gpui-dark-list-active); /* list_active — top plate */
|
||||
--gpui-stage-ink: var(--gpui-dark-text); /* foreground */
|
||||
--gpui-stage-ink-soft: var(--gpui-dark-text-muted); /* muted_foreground */
|
||||
--gpui-stage-ink-dim: var(--gpui-dark-text-muted); /* muted_foreground (AA, UX-05) */
|
||||
--gpui-stage-edge: var(--gpui-dark-border); /* border */
|
||||
--gpui-primary-dark: var(--gpui-dark-primary); /* primary */
|
||||
--gpui-primary-hover-dark: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity)); /* button_primary_hover */
|
||||
--gpui-accent-dark: var(--gpui-dark-accent); /* accent — hover wash */
|
||||
--gpui-selection-dark: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity)); /* selection */
|
||||
/* Legacy GPUI aliases, still read by unmigrated page partials. */
|
||||
--gpui-paper: var(--gpui-light-bg);
|
||||
--gpui-paper-raised: var(--gpui-light-panel);
|
||||
--gpui-paper-muted: var(--gpui-light-sidebar);
|
||||
--gpui-ink: var(--gpui-light-text);
|
||||
--gpui-ink-soft: var(--gpui-light-text-muted);
|
||||
--gpui-ink-mute: var(--gpui-light-text-muted);
|
||||
--gpui-edge: var(--gpui-light-border);
|
||||
--gpui-primary: var(--gpui-light-primary);
|
||||
--gpui-primary-hover: rgb(var(--gpui-light-primary-rgb) / var(--gpui-primary-hover-opacity));
|
||||
--gpui-stage: var(--gpui-dark-bg);
|
||||
--gpui-stage-deep: var(--gpui-dark-sidebar);
|
||||
--gpui-stage-muted: var(--gpui-dark-panel);
|
||||
--gpui-stage-raised: var(--gpui-dark-list-active);
|
||||
--gpui-stage-ink: var(--gpui-dark-text);
|
||||
--gpui-stage-ink-soft: var(--gpui-dark-text-muted);
|
||||
--gpui-stage-ink-dim: var(--gpui-dark-text-muted);
|
||||
--gpui-stage-edge: var(--gpui-dark-border);
|
||||
--gpui-primary-dark: var(--gpui-dark-primary);
|
||||
--gpui-primary-hover-dark: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity));
|
||||
--gpui-accent-dark: var(--gpui-dark-accent);
|
||||
--gpui-selection-dark: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity));
|
||||
--gpui-tan: #b1a17a;
|
||||
--gpui-moss: #739686;
|
||||
--gpui-rust: #aa6f6c;
|
||||
--c-stage-soft: var(--gpui-dark-text-muted-rgb);
|
||||
--c-stage-deep: var(--gpui-dark-sidebar-rgb);
|
||||
--c-primary-dark: var(--gpui-dark-primary-rgb);
|
||||
|
||||
/* Warm-muted state hues from the GPUI mockups, darkened or lifted per
|
||||
side of the waterline so text uses of them still clear AA. No
|
||||
set_theme slots: these are site-specific adaptations. */
|
||||
--gpui-tan: #b1a17a; /* human mark */
|
||||
--gpui-moss: #739686; /* outcome */
|
||||
--gpui-rust: #aa6f6c; /* attention */
|
||||
/* Type: Shannon Sans for every role, the system monospace stack for
|
||||
things a person could type or paste. No serif, no third family. */
|
||||
--font-body: var(--font-shannon-latin), var(--font-shannon-ext), var(--gpui-font-fallbacks),
|
||||
ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
--font-display: var(--font-body);
|
||||
--font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
"Liberation Mono", monospace;
|
||||
--font-cjk: var(--gpui-font-fallbacks), sans-serif;
|
||||
|
||||
/* RGB channel triples backing the Tailwind surface/ink tokens (see
|
||||
tailwind.config.ts). The dark block below re-points them. */
|
||||
/* Type scale (MEASUREMENTS §3.1). 12px is the floor: nothing renders
|
||||
smaller. Display sizes are website-only, one per page. */
|
||||
--text-caption: 0.75rem; /* 12/16 meta, timestamps, labels */
|
||||
--text-mono: var(--gpui-mono-size); /* 13/20 code, commands */
|
||||
--text-ui: 0.875rem; /* 14/20 controls, rows, panel body */
|
||||
--text-body: 1rem; /* 16/24 short copy, form inputs (no iOS zoom) */
|
||||
--text-prose: 1.0625rem; /* 17/26 reading: docs, legal, answers */
|
||||
--leading-prose: 1.53;
|
||||
--text-heading: 1.0625rem; /* 17/24 600 section heading */
|
||||
--text-title: 1.375rem; /* 22/28 600 page or sheet title */
|
||||
--text-display: clamp(1.75rem, 1.2rem + 2.2vw, 2.5rem); /* 28→40 inner-page hero */
|
||||
--text-display-lg: clamp(2.5rem, 1.6rem + 3.6vw, 3.5rem); /* 40→56 the home hero */
|
||||
/* Short aliases the spec uses. */
|
||||
--text-xs: var(--text-caption);
|
||||
--text-sm: var(--text-ui);
|
||||
--text-base: var(--text-body);
|
||||
|
||||
/* Space: a 4px program (MEASUREMENTS §4.1). */
|
||||
--space-1: 0.25rem; /* 4 */
|
||||
--space-2: 0.5rem; /* 8 */
|
||||
--space-3: 0.75rem; /* 12 */
|
||||
--space-4: 1rem; /* 16 */
|
||||
--space-5: 1.5rem; /* 24 */
|
||||
--space-6: 2rem; /* 32 */
|
||||
--space-7: 3rem; /* 48 */
|
||||
--space-8: 4rem; /* 64 */
|
||||
--section-gap: clamp(var(--space-7), 6vw, var(--space-8));
|
||||
|
||||
/* One container, one reading measure. --measure-prose is the GPUI
|
||||
PROSE_MEASURE (880px), not the 1440px READING_COLUMN. */
|
||||
--container: min(100% - var(--gpui-spacing-page), 76rem);
|
||||
--measure-prose: 55rem;
|
||||
--measure-text: 70ch;
|
||||
--hero-pad: clamp(3rem, 6vw, 4.75rem);
|
||||
--section-pad: clamp(2.75rem, 5vw, 4rem);
|
||||
|
||||
/* Radius: 6 controls, 10 surfaces (code, menus, cards), 14 sheets,
|
||||
pill for dots. No other radius. */
|
||||
--radius-control: var(--gpui-radius-control);
|
||||
--radius-surface: var(--gpui-radius-panel);
|
||||
--radius-sheet: 14px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
/* Motion (MEASUREMENTS §9.1). Nothing bounces: every curve stays inside
|
||||
[0, 1]. --ease-spring is the GPUI PANEL_SPRING fit (no overshoot). */
|
||||
--dur-state: 120ms; /* colour, border, opacity on hover and press */
|
||||
--dur-arrive: 180ms; /* something new arrives, rise <= 4px */
|
||||
--dur-exit: 120ms; /* something closes or leaves */
|
||||
--dur-spring: 340ms; /* panes and sheets */
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-spring: cubic-bezier(0.2, 0.1, 0.16, 1);
|
||||
--ease-exit: var(--ease-in);
|
||||
}
|
||||
|
||||
/* Reduced motion is a still pose: every duration steps. Keyframed motion is
|
||||
additionally declared only inside `prefers-reduced-motion:
|
||||
no-preference`, so it never starts. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root {
|
||||
--dur-state: 0ms;
|
||||
--dur-spring: 0ms;
|
||||
--dur-arrive: 0ms;
|
||||
--dur-exit: 0ms;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- light — the twilight floor on warm paper ---------- */
|
||||
:root {
|
||||
--bg: var(--gpui-light-bg);
|
||||
--surface: var(--gpui-light-sidebar);
|
||||
--panel: var(--gpui-light-panel);
|
||||
--text: var(--gpui-light-text);
|
||||
--muted: var(--gpui-light-text-muted);
|
||||
--line: var(--gpui-light-border);
|
||||
--line-strong: var(--gpui-light-border-strong);
|
||||
--accent: var(--gpui-light-primary);
|
||||
--accent-hover: rgb(var(--gpui-light-primary-rgb) / var(--gpui-primary-hover-opacity));
|
||||
--on-accent: var(--gpui-light-on-primary);
|
||||
--hover: var(--gpui-light-accent);
|
||||
--selected: var(--gpui-light-list-active);
|
||||
--selection: rgb(var(--gpui-light-primary-rgb) / var(--gpui-selection-opacity));
|
||||
--ring: var(--gpui-light-primary);
|
||||
--live: var(--gpui-light-live);
|
||||
--success: var(--gpui-light-live);
|
||||
--attention: var(--gpui-light-attention);
|
||||
--danger: var(--gpui-light-danger);
|
||||
--on-danger: var(--gpui-light-on-primary);
|
||||
--diff-add: rgb(104 152 119 / 0.14);
|
||||
--diff-del: rgb(189 119 116 / 0.11);
|
||||
--mark-ink: var(--gpui-light-text);
|
||||
/* Light in the dark: on paper the halo is a quiet ring. */
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 14%, transparent);
|
||||
--glow-focus: 0 0 0 6px rgb(var(--gpui-light-primary-rgb) / 0.12);
|
||||
--shadow-popover: 0 4px 8px rgb(0 0 0 / 0.16);
|
||||
/* The horizon needs a drawn edge only where the two grounds are within
|
||||
1.2:1 of each other — the dark appearance. */
|
||||
--horizon-edge: transparent;
|
||||
|
||||
/* Legacy names (sweep). */
|
||||
--c-paper: var(--gpui-light-bg-rgb);
|
||||
--c-paper-deep: var(--gpui-light-sidebar-rgb);
|
||||
--c-paper-edge: var(--gpui-light-border-rgb);
|
||||
@@ -60,11 +195,6 @@
|
||||
--c-ink-soft: var(--gpui-light-text-muted-rgb);
|
||||
--c-ink-mute: var(--gpui-light-text-muted-rgb);
|
||||
--c-indigo: var(--gpui-light-primary-rgb);
|
||||
--c-stage-soft: var(--gpui-dark-text-muted-rgb);
|
||||
--c-stage-deep: var(--gpui-dark-sidebar-rgb);
|
||||
--c-primary-dark: var(--gpui-dark-primary-rgb);
|
||||
|
||||
/* The sheet. */
|
||||
--paper: var(--gpui-paper);
|
||||
--paper-deep: var(--gpui-paper-muted);
|
||||
--paper-edge: var(--gpui-edge);
|
||||
@@ -74,28 +204,21 @@
|
||||
--ink: var(--gpui-ink);
|
||||
--ink-soft: var(--gpui-ink-soft);
|
||||
--ink-mute: var(--gpui-ink-mute);
|
||||
/* Action on paper is the GPUI light primary; hover sinks to its hover. */
|
||||
--indigo: var(--gpui-primary);
|
||||
--indigo-deep: var(--gpui-primary-hover);
|
||||
--indigo-pale: rgb(var(--c-indigo) / 0.1);
|
||||
--action-on-dark: var(--gpui-primary-dark);
|
||||
/* State inks darkened from the mockup hues so they clear AA on paper:
|
||||
moss is outcome, rust is attention, tan is the human mark. */
|
||||
--ochre: #7d6a3f;
|
||||
--jade: #4e6f61;
|
||||
--seafoam: var(--gpui-moss);
|
||||
--cobalt: var(--gpui-primary);
|
||||
--cyan: var(--gpui-primary); /* reserved for the composer prompt glyph and the release line */
|
||||
--cyan: var(--gpui-primary);
|
||||
--ocean-deep: var(--gpui-stage-deep);
|
||||
--ocean-mid: var(--gpui-stage-muted);
|
||||
--ocean-current: var(--gpui-primary-dark);
|
||||
--ocean-mist: var(--gpui-stage-ink-soft);
|
||||
--ocean-coral: #8f5a56;
|
||||
--signal-gold: #7d6a3f;
|
||||
/* The whale mark is ink on paper; below the waterline it is the light
|
||||
stage foreground. The warm tan stays reserved for human moments. */
|
||||
--mark-ink: var(--gpui-ink);
|
||||
/* Docs sheet inks: the sidebar's current link and the secondary button. */
|
||||
--docs-accent: var(--gpui-primary);
|
||||
--docs-button-bg: var(--gpui-paper-raised);
|
||||
--docs-button-border: var(--gpui-edge);
|
||||
@@ -103,124 +226,23 @@
|
||||
--stage-text: var(--gpui-stage-ink);
|
||||
--stage-soft: var(--gpui-stage-ink-soft);
|
||||
--stage-muted: var(--gpui-stage-ink-dim);
|
||||
|
||||
/* The water column, re-inked in the GPUI dark ramp. */
|
||||
--stage-field-top: var(--gpui-stage-muted); /* surface */
|
||||
--stage-field-mid: var(--gpui-stage); /* the authored 42% break */
|
||||
--stage-field-deep: var(--gpui-stage-deep); /* deep field, and the footer seabed */
|
||||
--stage-field-top: var(--gpui-stage-muted);
|
||||
--stage-field-mid: var(--gpui-stage);
|
||||
--stage-field-deep: var(--gpui-stage-deep);
|
||||
--stage-ambient: var(--gpui-accent-dark);
|
||||
--stage-composer: var(--gpui-stage-muted); /* the raised input plate */
|
||||
--stage-composer: var(--gpui-stage-muted);
|
||||
--stage-elevated: var(--gpui-stage-raised);
|
||||
--stage-line: var(--gpui-stage-edge); /* the charcoal border on stage */
|
||||
--stage-line: var(--gpui-stage-edge);
|
||||
--stage-hint: var(--gpui-stage-ink-dim);
|
||||
--stage-dim: var(--gpui-stage-ink-soft);
|
||||
--violet: var(--gpui-selection-dark); /* 1px rules only, never text */
|
||||
|
||||
/* Type, as GPUI set_theme: Shannon Sans for every role (the Latin and
|
||||
extended subsets load in app/[locale]/layout.tsx), the system monospace
|
||||
stack at 13px for code, prose at 15/23. Han, kana and
|
||||
Hangul fall through to the platform's sans. No serif anywhere. */
|
||||
--font-body: var(--font-shannon-latin), var(--font-shannon-ext), var(--gpui-font-fallbacks),
|
||||
ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
--font-display: var(--font-body);
|
||||
--font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
"Liberation Mono", monospace;
|
||||
--font-cjk: var(--gpui-font-fallbacks), sans-serif;
|
||||
--text-mono: var(--gpui-mono-size);
|
||||
--text-prose: 0.9375rem;
|
||||
--leading-prose: 1.5334;
|
||||
|
||||
/* Hairline + code surfaces routed through vars so the dark subtrees can
|
||||
re-ink them without touching every rule. Code plates are always the
|
||||
stage's own deepest field, on either side of the waterline. */
|
||||
--hairline: rgb(var(--c-ink) / 0.16);
|
||||
--code-bg: var(--gpui-stage-deep);
|
||||
--code-fg: var(--gpui-stage-ink);
|
||||
|
||||
/* One site container and one vertical rhythm. Every page gutter aligns
|
||||
with the nav; every hero and section shares a single padding token. */
|
||||
--container: min(100% - var(--gpui-spacing-page), 76rem);
|
||||
--hero-pad: clamp(3rem, 6vw, 4.75rem);
|
||||
--section-pad: clamp(2.75rem, 5vw, 4rem);
|
||||
|
||||
/* Radius grammar, as the GPUI client (codewhale-app/docs/DESIGN.md):
|
||||
6px for rows, chips and small controls; 10px for code and menu
|
||||
surfaces; 14px for the largest raised sheets; 999px for pills. No other
|
||||
radius appears in app/styles/ or the Tailwind scale. */
|
||||
--radius-control: var(--gpui-radius-control);
|
||||
--radius-surface: var(--gpui-radius-panel);
|
||||
--radius-sheet: 14px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
/* Motion. The GPUI panel spring is PANEL_SPRING (stiffness 420, damping
|
||||
42, mass 1; codewhale-app/src/workspace/motion.rs): damping ratio 1.02,
|
||||
so it never overshoots and reaches 99% at ~340ms. --ease-spring is
|
||||
that response fitted as one cubic-bezier (max error 1.3%). State
|
||||
changes (hover, colour, border) use the same curve at --dur-state;
|
||||
entrances run the full --dur-spring; exits accelerate away. */
|
||||
--ease-spring: cubic-bezier(0.2, 0.1, 0.16, 1);
|
||||
--ease-exit: cubic-bezier(0.4, 0, 0.8, 0.4);
|
||||
--dur-state: 150ms;
|
||||
--dur-spring: 340ms;
|
||||
--violet: var(--gpui-selection-dark);
|
||||
--hairline: var(--gpui-light-border);
|
||||
--code-bg: var(--gpui-light-sidebar);
|
||||
--code-fg: var(--gpui-light-text);
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
/* Reduced motion is a still pose, as the GPUI whale under the same setting:
|
||||
state changes step instead of blending, and every looping or entrance
|
||||
animation is gated behind `prefers-reduced-motion: no-preference` where
|
||||
it is declared. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root {
|
||||
--dur-state: 0ms;
|
||||
--dur-spring: 0ms;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------- role tokens — GPUI set_theme ---------- */
|
||||
/*
|
||||
* One token authority: the GPUI desktop client's `set_theme`
|
||||
* versioned design artifact (vendor/codewhale-design/tokens.json), generated into
|
||||
* ./tokens.css as `--gpui-dark-*` / `--gpui-light-*`. Nothing below repeats a
|
||||
* hex for a role: every role value is a generated token, or the primary at
|
||||
* the opacity `set_theme` derives (hover 0.9, selection 0.28).
|
||||
*
|
||||
* The role layer follows the OS: light by default, dark under
|
||||
* `prefers-color-scheme: dark` unless the page pins `data-theme="light"`,
|
||||
* and dark whenever it pins `data-theme="dark"`.
|
||||
*
|
||||
* --bg background the reading canvas
|
||||
* --surface sidebar recessed navigation and chrome
|
||||
* --panel muted raised inputs, cards, popovers
|
||||
* --text foreground
|
||||
* --muted muted_foreground secondary text (AA on --bg and --panel)
|
||||
* --line border
|
||||
* --accent primary action, links, whale blue
|
||||
* --on-accent primary_foreground
|
||||
* --hover accent hover wash, distinct from --panel
|
||||
* --selected list_active the current row
|
||||
* --selection primary @ 0.28 text selection
|
||||
* --ring primary focus ring
|
||||
*
|
||||
* Shoreline (`--shoreline-*`) is the TUI's charcoal theme and is not the
|
||||
* GPUI palette; `--whale-*`/`--light-*` remain available to anything that
|
||||
* genuinely wants the terminal palette.
|
||||
*/
|
||||
:root {
|
||||
--bg: var(--gpui-light-bg);
|
||||
--surface: var(--gpui-light-sidebar);
|
||||
--panel: var(--gpui-light-panel);
|
||||
--text: var(--gpui-light-text);
|
||||
--muted: var(--gpui-light-text-muted);
|
||||
--line: var(--gpui-light-border);
|
||||
--accent: var(--gpui-light-primary);
|
||||
--on-accent: var(--gpui-light-on-primary);
|
||||
--hover: var(--gpui-light-accent);
|
||||
--selected: var(--gpui-light-list-active);
|
||||
--selection: rgb(var(--gpui-light-primary-rgb) / var(--gpui-selection-opacity));
|
||||
--ring: var(--gpui-light-primary);
|
||||
}
|
||||
|
||||
/* ---------- dark — graphite; the OS-dark and pinned blocks match ---------- */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: var(--gpui-dark-bg);
|
||||
@@ -229,14 +251,27 @@
|
||||
--text: var(--gpui-dark-text);
|
||||
--muted: var(--gpui-dark-text-muted);
|
||||
--line: var(--gpui-dark-border);
|
||||
--line-strong: var(--gpui-dark-border-strong);
|
||||
--accent: var(--gpui-dark-primary);
|
||||
--accent-hover: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity));
|
||||
--on-accent: var(--gpui-dark-on-primary);
|
||||
--hover: var(--gpui-dark-accent);
|
||||
--selected: var(--gpui-dark-list-active);
|
||||
--selection: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity));
|
||||
--ring: var(--gpui-dark-primary);
|
||||
--live: var(--gpui-dark-live);
|
||||
--success: var(--gpui-dark-live);
|
||||
--attention: var(--gpui-dark-attention);
|
||||
--danger: var(--gpui-dark-danger);
|
||||
--on-danger: var(--gpui-dark-bg);
|
||||
--diff-add: rgb(36 61 69 / 0.22);
|
||||
--diff-del: rgb(116 72 84 / 0.15);
|
||||
--mark-ink: var(--gpui-dark-text);
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 18%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 42%, transparent);
|
||||
--glow-focus: 0 0 14px 2px rgb(var(--gpui-dark-primary-rgb) / 0.32);
|
||||
--shadow-popover: 0 4px 8px rgb(0 0 0 / 0.4);
|
||||
--horizon-edge: var(--gpui-dark-border);
|
||||
|
||||
/* The folio names, re-inked in the GPUI charcoal ramp. */
|
||||
--c-paper: var(--gpui-dark-bg-rgb);
|
||||
--c-paper-deep: var(--gpui-dark-panel-rgb);
|
||||
--c-paper-edge: var(--gpui-dark-border-rgb);
|
||||
@@ -261,22 +296,17 @@
|
||||
--cyan: var(--gpui-primary-dark);
|
||||
--ocean-coral: #d08a80;
|
||||
--signal-gold: #d6c78f;
|
||||
--mark-ink: var(--gpui-stage-ink);
|
||||
--docs-accent: var(--gpui-primary-dark);
|
||||
--docs-button-bg: var(--gpui-stage-muted);
|
||||
--docs-button-border: var(--gpui-stage-edge);
|
||||
--docs-button-text: var(--gpui-stage-ink);
|
||||
--hairline: rgb(var(--c-stage-soft) / 0.2);
|
||||
--hairline: var(--gpui-dark-border);
|
||||
--code-bg: var(--gpui-dark-sidebar);
|
||||
--code-fg: var(--gpui-dark-text);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
/* The pinned dark scheme repeats the OS-dark block exactly. The stage
|
||||
subtrees (the water column and the footer seabed) take the same block in
|
||||
either scheme, so a component never needs to know which side of the
|
||||
waterline it is on. */
|
||||
.ocean-column,
|
||||
.site-footer,
|
||||
:root[data-theme="dark"] {
|
||||
--bg: var(--gpui-dark-bg);
|
||||
--surface: var(--gpui-dark-sidebar);
|
||||
@@ -284,14 +314,27 @@
|
||||
--text: var(--gpui-dark-text);
|
||||
--muted: var(--gpui-dark-text-muted);
|
||||
--line: var(--gpui-dark-border);
|
||||
--line-strong: var(--gpui-dark-border-strong);
|
||||
--accent: var(--gpui-dark-primary);
|
||||
--accent-hover: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity));
|
||||
--on-accent: var(--gpui-dark-on-primary);
|
||||
--hover: var(--gpui-dark-accent);
|
||||
--selected: var(--gpui-dark-list-active);
|
||||
--selection: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity));
|
||||
--ring: var(--gpui-dark-primary);
|
||||
--live: var(--gpui-dark-live);
|
||||
--success: var(--gpui-dark-live);
|
||||
--attention: var(--gpui-dark-attention);
|
||||
--danger: var(--gpui-dark-danger);
|
||||
--on-danger: var(--gpui-dark-bg);
|
||||
--diff-add: rgb(36 61 69 / 0.22);
|
||||
--diff-del: rgb(116 72 84 / 0.15);
|
||||
--mark-ink: var(--gpui-dark-text);
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 18%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 42%, transparent);
|
||||
--glow-focus: 0 0 14px 2px rgb(var(--gpui-dark-primary-rgb) / 0.32);
|
||||
--shadow-popover: 0 4px 8px rgb(0 0 0 / 0.4);
|
||||
--horizon-edge: var(--gpui-dark-border);
|
||||
|
||||
/* The folio names, re-inked in the GPUI charcoal ramp. */
|
||||
--c-paper: var(--gpui-dark-bg-rgb);
|
||||
--c-paper-deep: var(--gpui-dark-panel-rgb);
|
||||
--c-paper-edge: var(--gpui-dark-border-rgb);
|
||||
@@ -316,11 +359,83 @@
|
||||
--cyan: var(--gpui-primary-dark);
|
||||
--ocean-coral: #d08a80;
|
||||
--signal-gold: #d6c78f;
|
||||
--mark-ink: var(--gpui-stage-ink);
|
||||
--docs-accent: var(--gpui-primary-dark);
|
||||
--docs-button-bg: var(--gpui-stage-muted);
|
||||
--docs-button-border: var(--gpui-stage-edge);
|
||||
--docs-button-text: var(--gpui-stage-ink);
|
||||
--hairline: rgb(var(--c-stage-soft) / 0.2);
|
||||
--hairline: var(--gpui-dark-border);
|
||||
--code-bg: var(--gpui-dark-sidebar);
|
||||
--code-fg: var(--gpui-dark-text);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* ---------- midnight — the stage below the horizon ---------- */
|
||||
/* Always the dark set, in both appearances (MEASUREMENTS §5.1): the footer
|
||||
and any home descent. The ground is the deepest graphite, so raised and
|
||||
recessed plates step up from it. `.ocean-column` stays until the home
|
||||
slice retires it. --horizon-edge is deliberately not redeclared: it
|
||||
follows the page's appearance. */
|
||||
.midnight,
|
||||
.site-footer,
|
||||
.ocean-column {
|
||||
--bg: var(--gpui-dark-sidebar);
|
||||
--surface: var(--gpui-dark-bg);
|
||||
--panel: var(--gpui-dark-panel);
|
||||
--text: var(--gpui-dark-text);
|
||||
--muted: var(--gpui-dark-text-muted);
|
||||
--line: var(--gpui-dark-border);
|
||||
--line-strong: var(--gpui-dark-border-strong);
|
||||
--accent: var(--gpui-dark-primary);
|
||||
--accent-hover: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity));
|
||||
--on-accent: var(--gpui-dark-on-primary);
|
||||
--hover: var(--gpui-dark-accent);
|
||||
--selected: var(--gpui-dark-list-active);
|
||||
--selection: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity));
|
||||
--ring: var(--gpui-dark-primary);
|
||||
--live: var(--gpui-dark-live);
|
||||
--success: var(--gpui-dark-live);
|
||||
--attention: var(--gpui-dark-attention);
|
||||
--danger: var(--gpui-dark-danger);
|
||||
--on-danger: var(--gpui-dark-bg);
|
||||
--diff-add: rgb(36 61 69 / 0.22);
|
||||
--diff-del: rgb(116 72 84 / 0.15);
|
||||
--mark-ink: var(--gpui-dark-text);
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 18%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 42%, transparent);
|
||||
--glow-focus: 0 0 14px 2px rgb(var(--gpui-dark-primary-rgb) / 0.32);
|
||||
--shadow-popover: 0 4px 8px rgb(0 0 0 / 0.4);
|
||||
|
||||
--c-paper: var(--gpui-dark-sidebar-rgb);
|
||||
--c-paper-deep: var(--gpui-dark-panel-rgb);
|
||||
--c-paper-edge: var(--gpui-dark-border-rgb);
|
||||
--c-ink: var(--gpui-dark-text-rgb);
|
||||
--c-ink-soft: var(--gpui-dark-text-muted-rgb);
|
||||
--c-ink-mute: var(--gpui-dark-text-muted-rgb);
|
||||
--c-indigo: var(--gpui-dark-primary-rgb);
|
||||
--paper: var(--gpui-stage-deep);
|
||||
--paper-deep: var(--gpui-stage-muted);
|
||||
--paper-edge: var(--gpui-stage-edge);
|
||||
--paper-card: var(--gpui-stage-muted);
|
||||
--paper-line: var(--gpui-stage-edge);
|
||||
--paper-line-soft: var(--gpui-stage-edge);
|
||||
--ink: var(--gpui-stage-ink);
|
||||
--ink-soft: var(--gpui-stage-ink-soft);
|
||||
--ink-mute: var(--gpui-stage-ink-dim);
|
||||
--indigo: var(--gpui-primary-dark);
|
||||
--indigo-deep: var(--gpui-primary-hover-dark);
|
||||
--indigo-pale: rgb(var(--c-primary-dark) / 0.14);
|
||||
--ochre: #d6c78f;
|
||||
--jade: #9ec7b2;
|
||||
--cyan: var(--gpui-primary-dark);
|
||||
--ocean-coral: #d08a80;
|
||||
--signal-gold: #d6c78f;
|
||||
--docs-accent: var(--gpui-primary-dark);
|
||||
--docs-button-bg: var(--gpui-stage-muted);
|
||||
--docs-button-border: var(--gpui-stage-edge);
|
||||
--docs-button-text: var(--gpui-stage-ink);
|
||||
--hairline: var(--gpui-dark-border);
|
||||
--code-bg: var(--gpui-dark-bg);
|
||||
--code-fg: var(--gpui-dark-text);
|
||||
color-scheme: dark;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
+34
-166
@@ -1,197 +1,65 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Motion — entrances, considered hovers, and one-shot settles */
|
||||
/* Utilities — focus, selection, links, the mobile sheet's motion */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/*
|
||||
* Grammar: the page is complete and static; motion answers a person's action
|
||||
* (a hover draws a rule, a press stamps a copy). The one authored ambient
|
||||
* moment is the column breath above. Everything here animates transform
|
||||
* and/or opacity only and is gated on `no-preference`, so reduced motion is
|
||||
* the *absence* of motion rather than an override fighting it.
|
||||
*/
|
||||
/* The nav-link underline draw, extended to the other mono-voice links. Hover
|
||||
draws the rule; the transition is the whole effect, so reduced motion keeps
|
||||
the existing colour change and simply skips the draw. */
|
||||
.product-start-links a,
|
||||
.gs-step-link,
|
||||
.product-surfaces > .product-container > a,
|
||||
.product-community nav a {
|
||||
position: relative;
|
||||
|
||||
/* ---------- focus + selection ---------- */
|
||||
/* The one focus treatment on the site: a 2px ring at a 3px offset in the
|
||||
accent of whatever depth the control sits in (midnight re-declares
|
||||
--ring). Components may add the bioluminescent halo (--glow-focus) as a
|
||||
box-shadow; none restyles or removes the ring. */
|
||||
::selection { background: var(--selection); }
|
||||
:focus-visible { outline: var(--gpui-focus-width) solid var(--ring); outline-offset: var(--gpui-focus-offset); }
|
||||
|
||||
/* ---------- links ---------- */
|
||||
a { color: inherit; text-decoration: none; }
|
||||
|
||||
/* A link in running text: accent ink, a quiet underline that firms on
|
||||
hover. `.body-link` is the legacy name for the same recipe. */
|
||||
.link,
|
||||
a.body-link {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.2em;
|
||||
text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
|
||||
transition: text-decoration-color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
.link:hover,
|
||||
a.body-link:hover { text-decoration-color: var(--accent); }
|
||||
|
||||
.product-start-links a::after,
|
||||
.gs-step-link::after,
|
||||
.product-surfaces > .product-container > a::after,
|
||||
.product-community nav a::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: -2px;
|
||||
height: 1px;
|
||||
background: currentColor;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform var(--dur-state) var(--ease-spring);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.product-start-links a:hover::after,
|
||||
.gs-step-link:hover::after,
|
||||
.product-surfaces > .product-container > a:hover::after,
|
||||
.product-community nav a:hover::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* The primary-button arrow leans into the direction it points. */
|
||||
.product-button > span[aria-hidden="true"] {
|
||||
display: inline-block;
|
||||
transition: transform var(--dur-state) var(--ease-spring);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.product-button:hover > span[aria-hidden="true"],
|
||||
.product-button:focus-visible > span[aria-hidden="true"] {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy-to-clipboard: the press is a stamp, the confirmation a jade seal —
|
||||
the wire's merge verb colour, borrowed for one beat. The colour change is
|
||||
unconditional (it is the confirmation); only the motion is gated. */
|
||||
.copy-btn[data-copied="true"] {
|
||||
background: var(--jade);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.copy-btn {
|
||||
transition: background-color var(--dur-state) var(--ease-spring), color var(--dur-state) var(--ease-spring), transform var(--dur-state) var(--ease-spring);
|
||||
}
|
||||
|
||||
.copy-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.copy-btn[data-copied="true"] {
|
||||
animation: copy-settle var(--dur-spring) var(--ease-spring);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes copy-settle {
|
||||
0% { transform: scale(1); }
|
||||
35% { transform: scale(0.93); }
|
||||
70% { transform: scale(1.04); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Mobile menu: the sheet drops a few points while its items settle in
|
||||
sequence; exit is a short fade. The exit is driven by a delayed unmount in
|
||||
components/mobile-menu.tsx — with reduced motion the menu mounts and
|
||||
unmounts instantly, exactly as before. */
|
||||
/* ---------- the mobile sheet ---------- */
|
||||
/* The sheet arrives (opacity plus a 4px rise) and leaves faster than it
|
||||
came. No per-item stagger. Reduced motion mounts and unmounts at once. */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.mm-panel {
|
||||
animation: mm-in var(--dur-spring) var(--ease-spring) both;
|
||||
animation: mm-in var(--dur-arrive) var(--ease-out) both;
|
||||
}
|
||||
|
||||
.mm-panel.mm-closing {
|
||||
animation: mm-out 170ms var(--ease-exit) both;
|
||||
animation: mm-out var(--dur-exit) var(--ease-in) both;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mm-panel li,
|
||||
.mm-panel nav > a {
|
||||
animation: mm-rise var(--dur-spring) var(--ease-spring) both;
|
||||
}
|
||||
|
||||
.mm-panel li:nth-child(1) { animation-delay: 34ms; }
|
||||
.mm-panel li:nth-child(2) { animation-delay: 58ms; }
|
||||
.mm-panel li:nth-child(3) { animation-delay: 82ms; }
|
||||
.mm-panel li:nth-child(4) { animation-delay: 106ms; }
|
||||
.mm-panel li:nth-child(5) { animation-delay: 130ms; }
|
||||
.mm-panel li:nth-child(6) { animation-delay: 154ms; }
|
||||
.mm-panel li:nth-child(7) { animation-delay: 178ms; }
|
||||
.mm-panel li:nth-child(8) { animation-delay: 202ms; }
|
||||
.mm-panel nav > a { animation-delay: 226ms; }
|
||||
}
|
||||
|
||||
@keyframes mm-in {
|
||||
from { opacity: 0; transform: translateY(-8px); }
|
||||
from { opacity: 0; transform: translateY(-0.25rem); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes mm-out {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-6px); }
|
||||
}
|
||||
|
||||
@keyframes mm-rise {
|
||||
from { opacity: 0; transform: translateY(6px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ---------- decorative big CJK in margin ---------- */
|
||||
.margin-glyph {
|
||||
font-family: var(--font-cjk);
|
||||
font-weight: 700;
|
||||
color: var(--ink);
|
||||
opacity: 0.04;
|
||||
font-size: 18rem;
|
||||
line-height: 0.9;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* ---------- focus + selection ---------- */
|
||||
/* The one focus treatment on the site: a 2px primary ring. No component
|
||||
restyles it; inputs keep the same ring rather than a glow. */
|
||||
::selection { background: var(--selection); }
|
||||
:focus-visible { outline: var(--gpui-focus-width) solid var(--ring); outline-offset: var(--gpui-focus-offset); }
|
||||
/* The dark stage keeps the light scheme's page but a sidebar-dark field;
|
||||
there the light primary ring is ~2.8:1 (#245bc7 on #191a1c), under the 3:1
|
||||
a focus indicator needs, so stage surfaces take the dark scheme's ring. */
|
||||
.ocean-column,
|
||||
.site-footer,
|
||||
.folio-shot,
|
||||
.portal-quickstart,
|
||||
.portal-community,
|
||||
.contribute-dev-loop,
|
||||
pre.code-block { --ring: var(--gpui-dark-primary); }
|
||||
|
||||
/* ---------- link reset ---------- */
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a.body-link {
|
||||
color: var(--ink);
|
||||
background-image: linear-gradient(var(--indigo), var(--indigo));
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 100%;
|
||||
background-size: 100% 1px;
|
||||
transition: background-size var(--dur-state) var(--ease-spring);
|
||||
}
|
||||
a.body-link:hover { background-size: 100% 6px; color: var(--ink); }
|
||||
|
||||
/* ---------- mobile-only adjustments ---------- */
|
||||
@media (max-width: 640px) {
|
||||
/* Big CJK margin glyph already hidden via tailwind's `hidden lg:block`,
|
||||
but re-assert that nothing hits the viewport edge. */
|
||||
.margin-glyph { display: none; }
|
||||
|
||||
/* Ticker text gets cramped on phones — shrink + tighten gaps */
|
||||
.ticker-track { gap: 1.5rem; padding-right: 1.5rem; }
|
||||
to { opacity: 0; transform: translateY(-0.25rem); }
|
||||
}
|
||||
|
||||
/* ---------- responsive chrome ---------- */
|
||||
@media (max-width: 900px) {
|
||||
.site-nav-inner { gap: 1rem; }
|
||||
.site-github-link { display: none; }
|
||||
.site-footer-main { grid-template-columns: 1fr; gap: 2.5rem; }
|
||||
.site-footer-meta { grid-template-columns: minmax(0, 1fr); gap: 0.7rem; }
|
||||
.site-footer-meta > div { min-width: 0; flex-wrap: wrap; }
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.site-wordmark { font-size: 1.05rem; }
|
||||
}
|
||||
|
||||
/* Anchor fallback; page-level scroll-mt utilities should be able to override it. */
|
||||
/* Anchor fallback; page-level scroll-mt utilities can override it. */
|
||||
:where([id]) { scroll-margin-top: 5rem; }
|
||||
@@ -265,6 +265,8 @@
|
||||
--gpui-dark-accent-rgb: 48 49 52;
|
||||
--gpui-dark-list-active: #37393d;
|
||||
--gpui-dark-list-active-rgb: 55 57 61;
|
||||
--gpui-dark-attention: #e8b077;
|
||||
--gpui-dark-attention-rgb: 232 176 119;
|
||||
--gpui-light-bg: #faf8f5;
|
||||
--gpui-light-bg-rgb: 250 248 245;
|
||||
--gpui-light-text: #28292b;
|
||||
@@ -285,6 +287,8 @@
|
||||
--gpui-light-accent-rgb: 232 229 224;
|
||||
--gpui-light-list-active: #dfdcd6;
|
||||
--gpui-light-list-active-rgb: 223 220 214;
|
||||
--gpui-light-attention: #86520d;
|
||||
--gpui-light-attention-rgb: 134 82 13;
|
||||
}
|
||||
/* GPUI design 1.0.0; sha256 a0c23f8a235bbc61e42fb8a60795ae7d9825dbbd1730142acc9ffec9c0abb3af. */
|
||||
:root {
|
||||
|
||||
Reference in new issue
Block a user