Skip to content

Commit 0a0710e

Browse files
www: Errors you can automate (#1763)
Replaces the homepage “Optimized for the edge” bundle chart with **Structured errors**. ## Copy lock - Hero: *Get a strictly typed env object from the validator you already use. No boilerplate. Zero dependencies.* - Feature 6 heading: **Structured errors** - Subtitle: Each issue gets a code that agents and CI can act on. Missing keys and bad values aren’t the same. - Widget: compact JSON (`success` + `issues[]`) for HOST `MISSING_VARIABLE` + PORT `INVALID_TYPE`. Messages collapse to `…` with a hover tooltip. Language `json` — no filename, no `$`, no `{ safe: true }` chrome, no twoslash. ## Scope - Not stacked on #1744. Fail-fast dump untouched. - Do **not** restore “Errors you can automate” / “Beautiful errors in the terminal” / “No additional dependencies” — those are the previous lock. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 776349f commit 0a0710e

6 files changed

Lines changed: 489 additions & 110 deletions

File tree

apps/www/app/(home)/aurora.css

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,7 +1853,9 @@
18531853

18541854
.home-aurora__payload-chip--port-valid,
18551855
.home-aurora__payload-chip--debug-true,
1856-
.home-aurora__payload-chip--debug-false {
1856+
.home-aurora__payload-chip--debug-false,
1857+
.home-aurora__payload-chip--theme-light,
1858+
.home-aurora__payload-chip--theme-dark {
18571859
color: var(--color-ink);
18581860
text-shadow: 0 0 8px color-mix(in oklch, var(--color-ink) 40%, transparent);
18591861
}
@@ -1864,7 +1866,9 @@
18641866

18651867
.home-aurora__payload-chip--port-coerced,
18661868
.home-aurora__payload-chip--debug-true-coerced,
1867-
.home-aurora__payload-chip--debug-false-coerced {
1869+
.home-aurora__payload-chip--debug-false-coerced,
1870+
.home-aurora__payload-chip--theme-light-coerced,
1871+
.home-aurora__payload-chip--theme-dark-coerced {
18681872
color: var(--color-accent);
18691873
text-shadow: 0 0 10px var(--color-accent);
18701874
}
@@ -1906,13 +1910,43 @@
19061910
animation: debug-dock-pulse-mobile 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 0s;
19071911
}
19081912

1913+
/* Row 3 Alternating Cadence (8s cycle, phase-shifted vs DEBUG) */
1914+
.home-aurora__payload-chip--theme-light {
1915+
animation: debug-travel-true 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 1.2s;
1916+
}
1917+
.home-aurora__payload-chip--theme-light-coerced {
1918+
animation: debug-travel-true-coerced 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 1.2s;
1919+
}
1920+
.home-aurora__payload-chip--theme-dark {
1921+
animation: debug-travel-false 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 1.2s;
1922+
}
1923+
.home-aurora__payload-chip--theme-dark-coerced {
1924+
animation: debug-travel-false-coerced 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 1.2s;
1925+
}
1926+
.home-aurora__payload-row--theme .home-aurora__payload-schema {
1927+
animation: debug-schema-flash 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 1.2s;
1928+
}
1929+
.home-aurora__payload-row--theme .home-aurora__payload-dock {
1930+
animation: debug-dock-pulse-mobile 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
1931+
}
1932+
.home-aurora__payload-row--theme .home-aurora__payload-col--left {
1933+
min-width: 4.5rem;
1934+
}
1935+
.home-aurora__payload-row--theme .home-aurora__payload-col--right {
1936+
min-width: 6.5rem;
1937+
justify-content: flex-end;
1938+
}
1939+
19091940
@container stream-stage (min-width: 28rem) {
19101941
.home-aurora__payload-row--port .home-aurora__payload-dock {
19111942
animation: port-dock-pulse 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 0s;
19121943
}
19131944
.home-aurora__payload-row--debug .home-aurora__payload-dock {
19141945
animation: debug-dock-pulse 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 0s;
19151946
}
1947+
.home-aurora__payload-row--theme .home-aurora__payload-dock {
1948+
animation: debug-dock-pulse 8s cubic-bezier(0.25, 1, 0.5, 1) infinite 1.2s;
1949+
}
19161950
}
19171951

19181952
/* Mobile direct wire carrier and tokens */
@@ -1978,6 +2012,26 @@
19782012
animation: debug-mobile-coerced-false 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 0s;
19792013
}
19802014

2015+
.home-aurora__payload-carrier--theme-light {
2016+
animation: debug-mobile-travel-true 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
2017+
}
2018+
.home-aurora__payload-carrier--theme-light .home-aurora__payload-val--raw {
2019+
animation: debug-mobile-raw-true 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
2020+
}
2021+
.home-aurora__payload-carrier--theme-light .home-aurora__payload-val--coerced {
2022+
animation: debug-mobile-coerced-true 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
2023+
}
2024+
2025+
.home-aurora__payload-carrier--theme-dark {
2026+
animation: debug-mobile-travel-false 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
2027+
}
2028+
.home-aurora__payload-carrier--theme-dark .home-aurora__payload-val--raw {
2029+
animation: debug-mobile-raw-false 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
2030+
}
2031+
.home-aurora__payload-carrier--theme-dark .home-aurora__payload-val--coerced {
2032+
animation: debug-mobile-coerced-false 8s cubic-bezier(0.2, 0.8, 0.25, 1) infinite 1.2s;
2033+
}
2034+
19812035
/* Keyframes for Row 1 Alternating Pipeline */
19822036
@keyframes port-travel-valid {
19832037
0% {
@@ -2888,6 +2942,68 @@
28882942
overflow-x: auto;
28892943
}
28902944

2945+
.home-aurora__tty--wrap {
2946+
white-space: pre-wrap;
2947+
overflow-wrap: anywhere;
2948+
overflow-x: hidden;
2949+
}
2950+
2951+
.home-aurora__json-ellipsis {
2952+
display: inline;
2953+
margin: 0;
2954+
padding: 0;
2955+
border: none;
2956+
border-bottom: 1px dotted color-mix(in oklch, var(--color-ink-2) 55%, transparent);
2957+
background: none;
2958+
font: inherit;
2959+
line-height: inherit;
2960+
color: inherit;
2961+
cursor: pointer;
2962+
border-radius: 2px;
2963+
}
2964+
2965+
@media (hover: hover) and (pointer: fine) {
2966+
.home-aurora__json-ellipsis {
2967+
cursor: help;
2968+
}
2969+
}
2970+
2971+
.home-aurora__json-ellipsis:focus-visible {
2972+
outline: 2px solid color-mix(in oklch, var(--color-accent) 70%, transparent);
2973+
outline-offset: 2px;
2974+
}
2975+
2976+
.home-aurora__json-ellipsis-tip {
2977+
font-family: var(--font-body);
2978+
word-break: break-word;
2979+
}
2980+
2981+
.home-aurora__json {
2982+
position: relative;
2983+
}
2984+
2985+
.home-aurora__json-lang {
2986+
position: absolute;
2987+
top: 0.55rem;
2988+
right: 0.75rem;
2989+
font-family: var(--font-body);
2990+
font-size: var(--text-xs);
2991+
line-height: 1;
2992+
color: var(--color-muted);
2993+
pointer-events: none;
2994+
letter-spacing: 0.02em;
2995+
}
2996+
2997+
.home-aurora__json-body {
2998+
margin: 0;
2999+
padding: var(--space-sm) var(--space-md);
3000+
font-family: var(--font-outlier);
3001+
font-size: var(--text-code);
3002+
line-height: 1.55;
3003+
color: var(--color-ink-2);
3004+
min-width: 0;
3005+
}
3006+
28913007
.home-aurora__tty .home-aurora__install-prompt-symbol {
28923008
font-size: inherit;
28933009
line-height: inherit;
@@ -3077,7 +3193,8 @@
30773193
height: 100%;
30783194
min-width: 0;
30793195
overflow: auto;
3080-
overscroll-behavior: contain;
3196+
/* Let the wheel continue the page once the pane hits an edge (no scroll trap). */
3197+
overscroll-behavior: auto;
30813198
scrollbar-width: thin;
30823199
scrollbar-color: color-mix(in oklch, var(--color-muted) 55%, transparent) transparent;
30833200
}

apps/www/app/(home)/page.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { fetchRoadmap } from "~/lib/roadmap/fetch-roadmap";
2424
export const metadata: Metadata = {
2525
title: "ArkEnv - Typesafe environment variables for TypeScript",
2626
description:
27-
"Typesafe environment variables with ArkType, Zod, Valibot, or any Standard Schema. Get a strictly typed env object using your existing TypeScript validator. No boilerplate. Zero runtime dependencies.",
27+
"Typesafe environment variables with ArkType, Zod, Valibot, or any Standard Schema. Get a strictly typed env object from the validator you already use. No boilerplate. Zero dependencies.",
2828
};
2929

3030
export const revalidate = 300;
@@ -63,9 +63,8 @@ export default async function HomePage() {
6363
className="home-aurora__summary rise-blur"
6464
style={{ animationDelay: "520ms" }}
6565
>
66-
Get a strictly typed <HeroEnvHoverSlot /> object using your
67-
existing TypeScript validator. No boilerplate. Zero runtime
68-
dependencies.
66+
Get a strictly typed <HeroEnvHoverSlot /> object from the
67+
validator you already use. No boilerplate. Zero dependencies.
6968
</p>
7069
</div>
7170
<div className="home-aurora__hero-ctas">

apps/www/components/page/declarative-showcase.test.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
33
import { DeclarativeShowcase } from "./declarative-showcase";
44

55
describe("DeclarativeShowcase", () => {
6-
it("renders the 2-row alternating payload pipeline with true/false debug toggles", () => {
6+
it("renders the 3-row alternating payload pipeline with enum THEME row", () => {
77
render(<DeclarativeShowcase />);
88

99
expect(
@@ -17,6 +17,7 @@ describe("DeclarativeShowcase", () => {
1717
// Static keys
1818
expect(figure).toHaveTextContent("PORT=");
1919
expect(figure).toHaveTextContent("DEBUG=");
20+
expect(figure).toHaveTextContent("THEME=");
2021

2122
// Row 1 alternating chips ("3000" success vs "oops" failure)
2223
expect(figure).toHaveTextContent('"3000"');
@@ -26,13 +27,19 @@ describe("DeclarativeShowcase", () => {
2627
expect(figure).toHaveTextContent('"true"');
2728
expect(figure).toHaveTextContent('"false"');
2829

30+
// Row 3 alternating chips ("light" vs "dark")
31+
expect(figure).toHaveTextContent('"light"');
32+
expect(figure).toHaveTextContent('"dark"');
33+
2934
// Schema Gates
3035
expect(figure).toHaveTextContent('"number"');
3136
expect(figure).toHaveTextContent('"boolean"');
37+
expect(figure).toHaveTextContent("\"'light' | 'dark'\"");
3238

3339
// Destination Type Definitions
3440
expect(figure).toHaveTextContent("number");
3541
expect(figure).toHaveTextContent("boolean");
42+
expect(figure).toHaveTextContent('"light" | "dark"');
3643
});
3744

3845
it("renders mobile direct single-wire elements and carrier tokens in the markup", () => {
@@ -41,7 +48,7 @@ describe("DeclarativeShowcase", () => {
4148
const directWires = container.querySelectorAll(
4249
".home-aurora__payload-wire--direct",
4350
);
44-
expect(directWires).toHaveLength(2);
51+
expect(directWires).toHaveLength(3);
4552

4653
const portCarrier = container.querySelector(
4754
".home-aurora__payload-carrier--port",
@@ -63,5 +70,17 @@ describe("DeclarativeShowcase", () => {
6370
expect(debugFalseCarrier).toBeInTheDocument();
6471
expect(debugFalseCarrier).toHaveTextContent('"false"');
6572
expect(debugFalseCarrier).toHaveTextContent("false");
73+
74+
const themeLightCarrier = container.querySelector(
75+
".home-aurora__payload-carrier--theme-light",
76+
);
77+
expect(themeLightCarrier).toBeInTheDocument();
78+
expect(themeLightCarrier).toHaveTextContent('"light"');
79+
80+
const themeDarkCarrier = container.querySelector(
81+
".home-aurora__payload-carrier--theme-dark",
82+
);
83+
expect(themeDarkCarrier).toBeInTheDocument();
84+
expect(themeDarkCarrier).toHaveTextContent('"dark"');
6685
});
6786
});

apps/www/components/page/declarative-showcase.tsx

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/**
22
* Automatic coercion showcase.
3-
* Minimalist 2-row Payload Pipeline:
3+
* Minimalist 3-row Payload Pipeline:
44
* - Row 1 (PORT=): Alternates between valid ("3000" -> 3000 number) and invalid ("oops" -> stuck at "number" -> error).
55
* - Row 2 (DEBUG=): Alternates between true ("true" -> true boolean) and false ("false" -> false boolean).
6+
* - Row 3 (THEME=): Alternates between "light" and "dark" enum members.
67
*/
78
export function DeclarativeShowcase() {
89
return (
@@ -254,6 +255,127 @@ export function DeclarativeShowcase() {
254255
<code className="home-aurora__payload-dock">boolean</code>
255256
</div>
256257
</div>
258+
259+
{/* Row 3: THEME= (alternates between "light" and "dark" enum members) */}
260+
<div className="home-aurora__payload-row home-aurora__payload-row--theme">
261+
{/* Left static key */}
262+
<div className="home-aurora__payload-col home-aurora__payload-col--left">
263+
<span className="home-aurora__payload-key">THEME=</span>
264+
</div>
265+
266+
{/* Mobile: Direct Wire (single track without middle gate) */}
267+
<div
268+
className="home-aurora__payload-wire home-aurora__payload-wire--direct"
269+
aria-hidden="true"
270+
>
271+
<svg
272+
className="home-aurora__stream-wire-svg"
273+
viewBox="0 0 100 20"
274+
preserveAspectRatio="none"
275+
aria-hidden="true"
276+
>
277+
<line
278+
x1="0"
279+
y1="10"
280+
x2="92"
281+
y2="10"
282+
className="home-aurora__stream-wire-track"
283+
/>
284+
<polygon
285+
points="90,7 98,10 90,13"
286+
className="home-aurora__stream-arrow"
287+
/>
288+
</svg>
289+
<div className="home-aurora__payload-carrier home-aurora__payload-carrier--theme-light">
290+
<span className="home-aurora__payload-val home-aurora__payload-val--raw">
291+
&quot;light&quot;
292+
</span>
293+
<span className="home-aurora__payload-val home-aurora__payload-val--coerced">
294+
&quot;light&quot;
295+
</span>
296+
</div>
297+
<div className="home-aurora__payload-carrier home-aurora__payload-carrier--theme-dark">
298+
<span className="home-aurora__payload-val home-aurora__payload-val--raw">
299+
&quot;dark&quot;
300+
</span>
301+
<span className="home-aurora__payload-val home-aurora__payload-val--coerced">
302+
&quot;dark&quot;
303+
</span>
304+
</div>
305+
</div>
306+
307+
{/* Wire 1 */}
308+
<div
309+
className="home-aurora__payload-wire home-aurora__payload-wire--1"
310+
aria-hidden="true"
311+
>
312+
<svg
313+
className="home-aurora__stream-wire-svg"
314+
viewBox="0 0 100 20"
315+
preserveAspectRatio="none"
316+
aria-hidden="true"
317+
>
318+
<line
319+
x1="0"
320+
y1="10"
321+
x2="100"
322+
y2="10"
323+
className="home-aurora__stream-wire-track"
324+
/>
325+
</svg>
326+
<span className="home-aurora__payload-chip home-aurora__payload-chip--theme-light">
327+
&quot;light&quot;
328+
</span>
329+
<span className="home-aurora__payload-chip home-aurora__payload-chip--theme-dark">
330+
&quot;dark&quot;
331+
</span>
332+
</div>
333+
334+
{/* Center Schema Gate */}
335+
<div className="home-aurora__payload-gate">
336+
<code className="home-aurora__payload-schema">
337+
&quot;&apos;light&apos; | &apos;dark&apos;&quot;
338+
</code>
339+
</div>
340+
341+
{/* Wire 2 */}
342+
<div
343+
className="home-aurora__payload-wire home-aurora__payload-wire--2"
344+
aria-hidden="true"
345+
>
346+
<svg
347+
className="home-aurora__stream-wire-svg"
348+
viewBox="0 0 100 20"
349+
preserveAspectRatio="none"
350+
aria-hidden="true"
351+
>
352+
<line
353+
x1="0"
354+
y1="10"
355+
x2="92"
356+
y2="10"
357+
className="home-aurora__stream-wire-track"
358+
/>
359+
<polygon
360+
points="90,7 98,10 90,13"
361+
className="home-aurora__stream-arrow"
362+
/>
363+
</svg>
364+
<span className="home-aurora__payload-chip home-aurora__payload-chip--theme-light-coerced">
365+
&quot;light&quot;
366+
</span>
367+
<span className="home-aurora__payload-chip home-aurora__payload-chip--theme-dark-coerced">
368+
&quot;dark&quot;
369+
</span>
370+
</div>
371+
372+
{/* Right destination dock */}
373+
<div className="home-aurora__payload-col home-aurora__payload-col--right">
374+
<code className="home-aurora__payload-dock">
375+
&quot;light&quot; | &quot;dark&quot;
376+
</code>
377+
</div>
378+
</div>
257379
</div>
258380
</figure>
259381
</section>

0 commit comments

Comments
 (0)