Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .impeccable/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"hook": {
"enabled": true,
"limits": {
"maxFindings": 5,
"maxChars": 8000
}
}
}
434 changes: 169 additions & 265 deletions .impeccable/design.json

Large diffs are not rendered by default.

417 changes: 232 additions & 185 deletions DESIGN.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/app.css

Large diffs are not rendered by default.

24 changes: 13 additions & 11 deletions public/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
(function () {
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];

// Tailwind classes for each HTTP method badge.
// Palette for each HTTP method badge. Every ink is built at one lightness and
// every wash at another, so the seven read as one family rather than as seven
// unrelated defaults. Defined in src/styles/theme.css.
const METHOD_CLASSES = {
GET: "bg-blue-50 text-blue-700",
POST: "bg-emerald-50 text-emerald-700",
PUT: "bg-amber-50 text-amber-700",
PATCH: "bg-violet-50 text-violet-700",
DELETE: "bg-rose-50 text-rose-700",
HEAD: "bg-slate-100 text-slate-700",
OPTIONS: "bg-sky-50 text-sky-700",
GET: "bg-get-wash text-get-ink",
POST: "bg-post-wash text-post-ink",
PUT: "bg-put-wash text-put-ink",
PATCH: "bg-patch-wash text-patch-ink",
DELETE: "bg-delete-wash text-delete-ink",
HEAD: "bg-head-wash text-head-ink",
OPTIONS: "bg-options-wash text-options-ink",
};

// How many cards are in the DOM at once, and how many more each reveal adds.
Expand Down Expand Up @@ -246,7 +248,7 @@
},

methodBadge(method) {
return METHOD_CLASSES[method] || "bg-slate-100 text-slate-700";
return METHOD_CLASSES[method] || "bg-head-wash text-head-ink";
},

formatTimeAgo: window.formatTimeAgo,
Expand Down Expand Up @@ -346,7 +348,7 @@
c.height = 32;
const ctx = c.getContext("2d");
// Soft background so the dot is visible against any browser tab style.
ctx.fillStyle = "#1e293b"; // slate-800
ctx.fillStyle = "#1e293b"; // neutral-800
ctx.beginPath();
ctx.arc(16, 16, 14, 0, 2 * Math.PI);
ctx.fill();
Expand All @@ -356,7 +358,7 @@
ctx.textBaseline = "middle";
ctx.fillText("h", 16, 17);
// Red badge in upper right
ctx.fillStyle = "#e11d48"; // rose-600
ctx.fillStyle = "#e11d48"; // danger-600
ctx.beginPath();
ctx.arc(24, 8, 7, 0, 2 * Math.PI);
ctx.fill();
Expand Down
1 change: 1 addition & 0 deletions src/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
to the shipped stylesheet, and editing .gitignore changes the build output. */
@import "tailwindcss" source(none);

@import "./theme.css";
@import "./base.css";
@import "./components.css";
@import "./motion.css";
Expand Down
2 changes: 1 addition & 1 deletion src/styles/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
}

:root {
accent-color: var(--color-indigo-600);
accent-color: var(--color-brand-600);
color-scheme: light;
}
}
56 changes: 33 additions & 23 deletions src/styles/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
Focus is one system, not two: every control uses :focus-visible so a mouse
click never paints a ring on a button, while text fields still show one
because browsers treat click-focus on them as keyboard modality. The two ring
weights are deliberate and recorded in DESIGN.md: 2px indigo-500 on controls,
1px indigo-400 plus a border shift on fields. */
weights are deliberate and recorded in DESIGN.md: 2px brand-500 on controls,
1px brand-400 plus a border shift on fields. */

@layer components {
/* Native select with the chevron drawn in, anchored right after the value.
The fill is a literal hex because a data URI cannot read a CSS variable;
it is the slate-500 equivalent and must be kept in step with it. */
it is the neutral-500 equivalent and must be kept in step with it. */
.app-select {
appearance: none;
-webkit-appearance: none;
Expand All @@ -35,83 +35,93 @@
/* Focus ring, applied by the component classes below. Exposed separately for
the handful of elements that are not buttons or fields. */
.focus-ring {
@apply focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500;
@apply focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500;
}

/* Buttons. One geometry, one focus treatment; variants change colour only. */
.btn {
@apply inline-flex items-center justify-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium;
@apply focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500;
@apply focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
}

.btn-secondary {
@apply border border-slate-300 bg-white text-slate-700 hover:bg-slate-50;
@apply border border-neutral-300 bg-white text-neutral-700;
@apply hover:bg-neutral-100 hover:border-neutral-400;
}

/* One destructive button, solid. A tinted variant shares its fill with the
danger surfaces it tends to sit on, so it stops reading as a button exactly
where the stakes are highest. */
.btn-danger {
@apply border border-rose-200 bg-rose-50 text-rose-700 hover:bg-rose-100;
@apply focus-visible:ring-rose-500;
@apply border border-transparent bg-danger-600 text-white hover:bg-danger-700;
@apply focus-visible:ring-danger-500;
}

.btn-primary {
@apply bg-indigo-600 px-4 py-2 text-white shadow-sm hover:bg-indigo-500;
@apply bg-brand-600 px-4 py-2 text-white hover:bg-brand-500;
@apply focus-visible:ring-offset-2;
}

/* The page's main action, where it is the reason the page exists. */
.btn-lg {
@apply px-6 py-3 text-base;
}

/* Text-only action inside a card. Padded to clear the 24px target minimum
without changing where the label sits. */
.btn-inline {
@apply inline-flex items-center gap-1 rounded-sm px-1 py-1 text-sm text-slate-500;
@apply hover:text-indigo-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500;
@apply inline-flex items-center gap-1 rounded-md px-1 py-1 text-sm text-neutral-500;
@apply hover:text-brand-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500;
}

.btn-inline-danger {
@apply hover:text-rose-600 focus-visible:ring-rose-500;
@apply hover:text-danger-600 focus-visible:ring-danger-500;
}

/* Form fields. The mono variant is for captured-data entry. */
.field {
@apply block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm;
@apply focus:outline-none focus-visible:ring-1 focus-visible:ring-indigo-400 focus-visible:border-indigo-500;
@apply block w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm;
@apply focus:outline-none focus-visible:ring-1 focus-visible:ring-brand-400 focus-visible:border-brand-500;
}

.field-mono {
@apply font-mono;
}

/* Instrument label: uppercase, for the capture surfaces. */
.field-label {
@apply block text-xs font-medium text-slate-500 uppercase tracking-wide mb-1;
@apply block text-xs font-medium text-neutral-500 uppercase tracking-wide mb-2;
}

/* Section label inside a card. Same token as a field label with the spacing
a region heading needs. */
.region-label {
@apply text-xs font-medium text-slate-500 uppercase tracking-wide mb-2;
@apply text-xs font-medium text-neutral-500 uppercase tracking-wide mb-2;
}

/* Surfaces. panel carries the resting whisper; panel-flat is the same edge
without it, for surfaces that sit inside another panel. */
.panel {
@apply rounded-lg border border-slate-200 bg-white shadow-sm;
@apply rounded-lg border border-neutral-200 bg-white;
}

.panel-flat {
@apply rounded-lg border border-slate-200 bg-white;
@apply rounded-lg border border-neutral-200 bg-white;
}

/* Key/value row. Stacks below the breakpoint so a value is never squeezed
into what is left of a fixed label column on a phone. */
.kv-row {
@apply flex flex-col sm:flex-row sm:gap-3 py-1.5 border-b border-slate-100 last:border-b-0;
@apply flex flex-col sm:flex-row sm:gap-3 py-1.5 border-b border-neutral-100 last:border-b-0;
}

.kv-row-tight {
@apply py-1;
}

.kv-key {
@apply text-slate-500 sm:w-40 sm:shrink-0;
@apply text-neutral-500 sm:w-40 sm:shrink-0;
}

.kv-value {
Expand All @@ -127,12 +137,12 @@
}

.code-block {
@apply text-xs font-mono bg-slate-50 border border-slate-200 rounded p-3 overflow-auto;
@apply text-xs font-mono bg-neutral-50 border border-neutral-200 rounded p-3 overflow-auto;
}

/* Absent value. slate-500 rather than slate-400: the lighter step measured
/* Absent value. neutral-500 rather than neutral-400: the lighter step measured
2.63:1 on white, under the 4.5:1 floor. */
.empty-value {
@apply text-xs text-slate-500 italic;
@apply text-xs text-neutral-500 italic;
}
}
85 changes: 85 additions & 0 deletions src/styles/theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* The palette. Authored in OKLCH rather than taken from Tailwind's defaults, so
the greys, the accent and the method spectrum belong to one family.

The neutral ramp sits on the mark's hue at very low chroma, which is why the
greys read as related to the brand instead of as a generic blue-grey. The
brand ramp carries the mark's hue and its chroma; the mark itself is too
light to hold white text (3.6:1), so the fill is the same colour darkened
until it passes, and #707ee7 stays exactly as it is in the logo.

The method spectrum keeps conventional meanings, GET blue and POST green and
DELETE red, but every ink is built at one lightness and every wash at
another, so the seven badges carry equal weight instead of inheriting seven
unrelated Tailwind defaults. */

@theme {
/* Clear Tailwind's default palette: every colour in this product is
authored below, so a stock utility cannot leak in unnoticed. */
--color-*: initial;

--color-white: #fff;
--color-black: #000;
--color-transparent: transparent;
--color-current: currentColor;

/* Neutral chassis */
--color-neutral-50: oklch(98.4% 0.003 274);
--color-neutral-100: oklch(96.6% 0.006 274);
--color-neutral-200: oklch(92.8% 0.011 274);
--color-neutral-300: oklch(86.6% 0.018 274);
--color-neutral-400: oklch(70.2% 0.032 274);
--color-neutral-500: oklch(55.2% 0.038 274);
--color-neutral-600: oklch(44.4% 0.036 274);
--color-neutral-700: oklch(37% 0.034 274);
--color-neutral-800: oklch(27.8% 0.032 274);
--color-neutral-900: oklch(20.6% 0.03 274);
--color-neutral-950: oklch(12.8% 0.026 274);

/* Brand, anchored on the mark */
--color-brand-50: oklch(96.5% 0.022 275.25);
--color-brand-100: oklch(93.2% 0.042 275.25);
--color-brand-200: oklch(87.5% 0.075 275.25);
--color-brand-300: oklch(79.5% 0.11 275.25);
--color-brand-400: oklch(70% 0.14 275.25);
--color-brand-500: oklch(57.5% 0.157 275.25);
--color-brand-600: oklch(52% 0.157 275.25);
--color-brand-700: oklch(46% 0.15 275.25);
--color-brand-800: oklch(39% 0.13 275.25);
--color-brand-900: oklch(33% 0.105 275.25);
--color-mark: #707ee7;

/* Method spectrum */
--color-get-ink: oklch(50% 0.155 255);
--color-get-wash: oklch(97% 0.025 255);
--color-post-ink: oklch(50% 0.115 157);
--color-post-wash: oklch(97% 0.018 157);
--color-put-ink: oklch(50% 0.135 62);
--color-put-wash: oklch(97% 0.022 62);
--color-patch-ink: oklch(50% 0.165 308);
--color-patch-wash: oklch(97% 0.026 308);
--color-delete-ink: oklch(50% 0.17 19);
--color-delete-wash: oklch(97% 0.027 19);
--color-options-ink: oklch(50% 0.13 213);
--color-options-wash: oklch(97% 0.021 213);
--color-head-ink: oklch(37% 0.034 274);
--color-head-wash: oklch(96.6% 0.006 274);

/* Syntax highlighting. Vendored from the highlight.js GitHub theme rather
than authored here, so these are plain sRGB and deliberately outside the
OKLCH families above. They are tokens so the marketing page's static
example can reference them instead of hard-coding hex. */
--color-syntax-key: #005cc5;
--color-syntax-string: #032f62;
--color-syntax-name: #22863a;
--color-syntax-keyword: #d73a49;

/* Destructive and stream status */
--color-danger-50: oklch(97% 0.02 19);
--color-danger-100: oklch(94% 0.04 19);
--color-danger-200: oklch(89% 0.07 19);
--color-danger-500: oklch(62% 0.19 19);
--color-danger-600: oklch(55% 0.185 19);
--color-danger-700: oklch(48% 0.165 19);
--color-live: oklch(62% 0.145 157);
--color-pending: oklch(68% 0.145 62);
}
Loading
Loading