返回 CodeWhale
strata.tsx
根目录 / web / components / strata.tsx
1 /**
2 * <Strata> — the folio's water, drawn as geometry.
3 *
4 * Four translucent strata of the GPUI palette stacked from the light end of
5 * the ombre down to the deep charcoal field, each edge softened the way an
6 * ink wash bleeds into paper, with a few fine current lines in the primary
7 * blue and the human tan riding the crests. Every colour is a palette token
8 * read from the cascade (`currentColor` and the `--gpui-*` custom
9 * properties), so the drawing re-inks with the theme and never carries a
10 * hex of its own.
11 *
12 * Two compositions share one vocabulary:
13 *
14 * hero — the water rises from the bottom right of the plate and leaves the
15 * top left as paper for the title. Anchored to the plate's floor.
16 * band — a horizontal waterline: paper above, deep field below. Used at
17 * the homepage descent and before the footer on every other page.
18 *
19 * Decorative and static: `aria-hidden`, no animation, no interaction. This
20 * is the one illustrated element on the site; nothing else is drawn.
21 */
22
23 type Variant = "hero" | "band";
24
25 const FILTERS = (
26 <defs>
27 <filter id="cw-wash-wide" x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
28 <feGaussianBlur stdDeviation="34" />
29 </filter>
30 <filter id="cw-wash" x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
31 <feGaussianBlur stdDeviation="20" />
32 </filter>
33 <filter id="cw-wash-tight" x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
34 <feGaussianBlur stdDeviation="11" />
35 </filter>
36 <filter id="cw-wash-edge" x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
37 <feGaussianBlur stdDeviation="5" />
38 </filter>
39 </defs>
40 );
41
42 function HeroStrata() {
43 return (
44 <svg
45 className="folio-strata"
46 viewBox="0 0 1440 1000"
47 preserveAspectRatio="xMidYMax slice"
48 aria-hidden="true"
49 focusable="false"
50 >
51 {FILTERS}
52 {/* Paper haze — a breath of the primary's light end over the top right
53 so the sheet is not flat where the water will rise. */}
54 <ellipse cx="1180" cy="120" rx="520" ry="200" fill="var(--gpui-primary-dark)" opacity="0.4" filter="url(#cw-wash-wide)" />
55 {/* Sky — the light end of the ombre, the first stratum. */}
56 <path
57 d="M -240 690 C 160 640, 420 720, 660 570 C 880 430, 1040 280, 1240 210 C 1340 175, 1440 150, 1700 110 L 1700 1120 L -240 1120 Z"
58 fill="var(--gpui-primary-dark)"
59 opacity="0.42"
60 filter="url(#cw-wash-wide)"
61 />
62 {/* Mid water. */}
63 <path
64 d="M -240 800 C 140 770, 400 830, 620 700 C 830 575, 1000 440, 1200 370 C 1330 325, 1500 280, 1700 250 L 1700 1120 L -240 1120 Z"
65 fill="var(--gpui-stage-raised)"
66 opacity="0.62"
67 filter="url(#cw-wash)"
68 />
69 {/* The stage plate's muted surface. */}
70 <path
71 d="M -240 890 C 200 860, 430 905, 650 810 C 870 715, 1060 580, 1260 520 C 1400 478, 1540 445, 1700 410 L 1700 1120 L -240 1120 Z"
72 fill="var(--gpui-stage-muted)"
73 opacity="0.9"
74 filter="url(#cw-wash-tight)"
75 />
76 {/* The deep field. Its top edge is the crispest, the way the darkest
77 wash dries with a line. */}
78 <path
79 d="M -240 965 C 220 945, 470 975, 710 910 C 910 855, 1110 740, 1310 680 C 1430 645, 1560 615, 1700 1120 L 1700 1120 L -240 1120 Z"
80 fill="var(--gpui-stage-deep)"
81 filter="url(#cw-wash-edge)"
82 />
83 {/* Current lines: fine strokes riding the crests. Primary blue on the
84 water, ink on the paper, one tan thread for the human mark. */}
85 <g fill="none" strokeLinecap="round">
86 <path d="M 980 330 C 1100 285, 1200 255, 1340 215 C 1420 192, 1520 170, 1700 140" stroke="var(--gpui-primary-dark)" strokeOpacity="0.5" strokeWidth="1.1" />
87 <path d="M 760 640 C 900 560, 1040 470, 1200 405 C 1330 352, 1480 310, 1700 275" stroke="var(--gpui-primary-dark)" strokeOpacity="0.36" strokeWidth="1" />
88 <path d="M 620 860 C 840 780, 1040 660, 1240 590 C 1400 535, 1560 500, 1700 470" stroke="var(--gpui-primary-dark)" strokeOpacity="0.3" strokeWidth="1" />
89 <path d="M 420 960 C 640 930, 860 860, 1060 780 C 1240 708, 1420 650, 1700 600" stroke="var(--gpui-tan)" strokeOpacity="0.45" strokeWidth="0.9" />
90 <path d="M 1010 60 C 1100 110, 1140 180, 1120 260" stroke="var(--ink)" strokeOpacity="0.14" strokeWidth="0.9" />
91 <path d="M 1060 40 C 1170 100, 1230 190, 1210 300 C 1200 340, 1180 370, 1150 400" stroke="var(--ink)" strokeOpacity="0.12" strokeWidth="0.9" />
92 <path d="M 1150 20 C 1290 80, 1360 170, 1340 300" stroke="var(--ink)" strokeOpacity="0.1" strokeWidth="0.9" />
93 </g>
94 </svg>
95 );
96 }
97
98 function BandStrata() {
99 return (
100 <svg
101 viewBox="0 0 1600 400"
102 preserveAspectRatio="xMidYMid slice"
103 aria-hidden="true"
104 focusable="false"
105 >
106 {FILTERS}
107 <path
108 d="M -160 150 C 220 110, 520 190, 820 140 C 1100 95, 1340 160, 1760 120 L 1760 520 L -160 520 Z"
109 fill="var(--gpui-primary-dark)"
110 opacity="0.45"
111 filter="url(#cw-wash-wide)"
112 />
113 <path
114 d="M -160 220 C 240 190, 520 250, 820 215 C 1100 180, 1360 235, 1760 200 L 1760 520 L -160 520 Z"
115 fill="var(--gpui-stage-raised)"
116 opacity="0.7"
117 filter="url(#cw-wash)"
118 />
119 <path
120 d="M -160 285 C 240 260, 540 305, 840 280 C 1120 255, 1380 300, 1760 270 L 1760 520 L -160 520 Z"
121 fill="var(--gpui-stage-muted)"
122 opacity="0.92"
123 filter="url(#cw-wash-tight)"
124 />
125 <path
126 d="M -160 345 C 240 325, 560 360, 860 340 C 1140 322, 1400 352, 1760 335 L 1760 520 L -160 520 Z"
127 fill="var(--gpui-stage)"
128 filter="url(#cw-wash-edge)"
129 />
130 <g fill="none" strokeLinecap="round">
131 <path d="M -40 175 C 300 140, 560 205, 860 165 C 1120 130, 1380 180, 1660 150" stroke="var(--gpui-primary-dark)" strokeOpacity="0.45" strokeWidth="1.1" />
132 <path d="M 120 250 C 420 225, 700 270, 980 245 C 1220 223, 1440 262, 1680 235" stroke="var(--gpui-primary-dark)" strokeOpacity="0.3" strokeWidth="1" />
133 <path d="M -40 320 C 300 300, 620 335, 900 315 C 1160 297, 1420 330, 1680 305" stroke="var(--gpui-tan)" strokeOpacity="0.42" strokeWidth="0.9" />
134 </g>
135 </svg>
136 );
137 }
138
139 export function Strata({ variant }: { variant: Variant }) {
140 return variant === "hero" ? <HeroStrata /> : <BandStrata />;
141 }
142
142 lines Plain Text