git.iko.soy/rowtype-yoga/psfp
on 84c60dc help sign in
958 lines 31 kB history raw
1module SVG.Image where
3import Prelude
4import CSS.Safer (cssSafer)
5import DOM.Hook (useBoundingBox)
6import Data.Array (elem, foldl, head, intercalate, snoc, uncons, zip, (:))
7import Data.Int (round, toNumber)
8import Data.Maybe (Maybe(..), maybe)
9import Data.String (codePointFromChar)
10import Data.String as String
11import Data.Tuple.Nested ((/\))
12import Effect (Effect)
13import Random.PseudoRandom (mkSeed, randomRs)
14import React.Basic (JSX, element)
15import React.Basic.DOM (unsafeCreateDOMComponent)
16import React.Basic.DOM as HTML
17import React.Basic.DOM.SVG as SVG
18import React.Basic.Hooks (ReactComponent)
19import React.Basic.Hooks as React
20import SVG.Icon (Raw)
21import Scroll.Hook (useScrollYPosition)
22import Theme.Styles (makeStyles, useTheme)
23import Theme.Types (CSSTheme)
25foreign import landingPageDarkRaw ∷ ∀ r. Raw r
27foreign import landingPageLightRaw ∷ ∀ r. Raw r
29mkLandingPageBackground ∷ Effect (ReactComponent { className ∷ String })
30mkLandingPageBackground = do
31 useStyles <-
32 makeStyles \(theme ∷ CSSTheme) ->
33 { laptopBackground:
34 cssSafer
35 { fill: theme.backgroundColour
36 }
37 , lightEllipsis:
38 cssSafer
39 { cx: if theme.isLight then "721" else "175"
40 , cy: if theme.isLight then "355" else "310"
41 , rx: if theme.isLight then "647" else "330"
42 , ry: if theme.isLight then "310" else "252"
43 }
44 , laptopBody:
45 cssSafer
46 { fill: if theme.isLight then "url(#_Linear9)" else "url(#_Linear12)"
47 }
48 , screenText:
49 cssSafer
50 { fontFamily: "PragmataPro"
51 , fontSize: "0.85em"
52 , fill: theme.textColour
53 }
54 , keyword: cssSafer { fill: theme.green }
55 , typeName: cssSafer { fill: theme.pink }
56 , "@keyframes animatedCursor":
57 cssSafer
58 { "0%": { opacity: "0.0" }
59 , "50%": { opacity: "1.0" }
60 }
61 , cursor:
62 cssSafer
63 { animation: "$animatedCursor 0.5s linear infinite"
64 , animationDirection: "alternate"
65 }
66 , "@keyframes movingCloud1":
67 cssSafer
68 { "0%": { transform: "translate3d(-140%, 4%, 0)" }
69 , "100%": { transform: "translate3d(70%, -4%, 0)" }
70 }
71 , movingCloud1:
72 cssSafer
73 { animation: "$movingCloud1 200s linear infinite"
74 , animationDelay: "-150s"
75 }
76 , "@keyframes movingCloud2":
77 cssSafer
78 { "0%": { transform: "translate3d(-10%, 0%, 0)" }
79 , "100%": { transform: "translate3d(10%, 0%, 0)" }
80 }
81 , movingCloud2:
82 cssSafer
83 { animation: "$movingCloud2 120s linear infinite"
84 , animationDirection: "alternate"
85 }
86 , movingCloud3:
87 cssSafer
88 { animation: "$movingCloud2 120s linear infinite"
89 , animationDelay: "-120s"
90 , animationDirection: "alternate"
91 }
92 , "@keyframes twinkle":
93 cssSafer
94 { "0%": { fill: "rgba(250, 250, 250, 0.2)" }
95 , "50%": { fill: "rgba(250, 250, 250, 0.7)" }
96 , "70%": { fill: "rgba(250, 250, 250, 0.4)" }
97 , "100%": { fill: "rgba(250, 250, 250, 1.0)" }
98 }
99 , "@keyframes twinkleFast":
100 cssSafer
101 { "0%": { fill: "rgba(250, 250, 250, 0.7)" }
102 , "100%": { fill: "rgba(250, 250, 250, 0.78)" }
103 }
104 , twinklingStar1:
105 cssSafer
106 { animation: "$twinkleFast 0.2s ease-in-out infinite"
107 , animationDirection: "alternate"
108 }
109 , twinklingStar2:
110 cssSafer
111 { animation: "$twinkle 8s ease-in-out infinite"
112 , animationDelay: "4s"
113 , animationDirection: "alternate"
114 }
115 , twinklingStar3:
116 cssSafer
117 { animation: "$twinkle 4s ease-in-out infinite"
118 , animationDelay: "3s"
119 , animationDirection: "alternate"
120 }
121 , "@keyframes rotateStars":
122 cssSafer
123 { "0%": { transform: "rotate(0deg) scale(1,1)" }
124 , "100%": { transform: "rotate(1deg) scale(1.01, 1.01)" }
125 }
126 , stars:
127 cssSafer
128 { animation: "$rotateStars 30s linear infinite"
129 , transformOrigin: "top center"
130 , animationDirection: "alternate"
131 }
132 }
133 React.component "LandingPageBackground" \{ className } -> React.do
134 theme <- useTheme
135 classes <- useStyles
136 scrollY <- useScrollYPosition
137 bb /\ ref <- useBoundingBox
138 let
139 scrolled = case bb of
140 Nothing -> 0.0
141 Just vh -> scrollY / vh.height
142 pure
143 $ element (unsafeCreateDOMComponent "svg") -- [TODO] make PR for react-basic to allow for `ref` on svg
144 { ref
145 , viewBox: "0 0 800 447"
146 , preserveAspectRatio: "xMidYMax slice"
147 , xmlns: "http://www.w3.org/2000/svg"
148 , fillRule: "evenodd"
149 , clipRule: "evenodd"
150 , strokeLinejoin: "round"
151 , strokeMiterlimit: "2"
152 , className
153 , children:
154 [ SVG.path
155 { fill: "none"
156 , d: "M0 0h800v447H0z"
157 }
158 , SVG.clipPath
159 { id: "a"
160 , children:
161 [ SVG.path
162 { d: "M0 0h800v447H0z"
163 }
164 ]
165 }
166 , SVG.g
167 { clipPath: "url(#a)"
168 , children:
169 [ SVG.path
170 { d: "M800.828 448.051V-3.513H-3.028v451.564h803.856z"
171 , fill: "url(#_Linear2)"
172 }
173 , SVG.ellipse
174 { className: classes.lightEllipsis
175 , fill: "url(#_Radial3)"
176 }
177 , stars classes.stars classes
178 -- bgcloud
179 , SVG.path
180 { d: "M610.291 177.316c-35.879-.428-84.67-.691-138.392-.689-38.684.002-74.811.141-105.402.38-49.14-2.745-80.877-9.954-80.877-14.718 0-1.333 2.655.335 7.294-.896 96.772-.175 171.302-1.213 171.302-2.464 0-1.107-59.462-3.372-140.106-3.687 38.487-2.059 102.543-4.972 173.823-4.972 11.996 0 23.761.07 35.216.206 20.76-4.638 60.945-2.784 107.07-2.784 36.08 0 68.525-.075 90.882 2.983 32.491.899 72.142-1.186 114.922-.379 38.68.729 74.805 1.273 105.395 1.613 49.101 3.673 80.599 8.516 80.54 13.279-.016 1.334-2.504 2.58-7.158 3.723-96.763-1.654-171.297-2.024-171.312-.774-.013 1.108 58.473 3.15 139.105 4.989-38.514 3.085-101.653 4.38-172.925 3.033-11.995-.227-23.757-.52-35.209-.872-20.815 4.245-61.034 6.631-107.154 5.759-24.754-.468-47.786-1.809-67.014-3.73z"
181 , fill: if theme.isLight then "#ddf0fb" else "#240c3c"
182 , className: classes.movingCloud1
183 , transform: "translate(0 " <> show (scrolled * 20.0) <> ")"
184 }
185 -- bgcloud
186 , SVG.path
187 { d: "M609.299 171.607c-35.88-.338-84.67-.545-138.392-.543-38.684.001-74.812.111-105.402.299-49.141-2.166-80.694-5.519-80.694-9.279 0-1.052 2.472-2.073 7.111-3.045 96.772-.137 171.302-.957 171.302-1.944-.001-.874-58.506-1.614-139.15-1.862 38.471-3.009 101.587-4.973 172.867-4.973 11.996 0 23.761.055 35.216.162 20.76-3.66 60.945-6.143 107.07-6.143 36.08 0 68.525 1.519 90.882 3.933 32.491.709 72.142 1.432 114.922 2.069 38.68.575 74.805 1.005 105.395 1.272 49.101 2.9 80.598 6.722 80.54 10.482-.017 1.052-2.505 2.036-7.158 2.938-96.763-1.305-171.297-1.597-171.312-.611-.014.875 58.473 2.486 139.104 3.938-38.514 2.435-101.653 3.457-172.924 2.394a3771.84 3771.84 0 01-35.21-.688c-20.814 3.35-61.033 5.234-107.153 4.545-24.754-.369-47.786-1.428-67.014-2.944z"
188 , fill: if theme.isLight then "#d8effd" else "#120b38"
189 , className: classes.movingCloud1
190 , transform: "translate(0 " <> show (scrolled * 20.0) <> ")"
191 }
192 -- rightcloud
193 , SVG.path
194 { d: "M684.234 222.269c-12.108-1.642-29.721-2.832-29.76-5.218-.063-3.861 34.226-4.225 76.6-3.504 42.375.721 75.9-2.707 76.956 1.008 2.476 8.704-12.549 8.552-32.56 10.141-12.403-.58-28.24.216-44.434.479-16.193.263-35.197-1.331-46.802-2.906z"
195 , fill: if theme.isLight then "#d8effd" else "url(#_Linear4)"
196 , className: classes.movingCloud2
197 , transform: "translate(0 " <> show (scrolled * 20.0) <> ")"
198 }
199 -- leftcloud
200 , SVG.path
201 { d: "M130.064 213.821c42.381 0 76.789.237 76.789 4.224 0 3.986-14.408 4.223-56.789 4.223-42.38 0-96.788.208-96.788-4.223s34.408-4.224 76.788-4.224z"
202 , fill: if theme.isLight then "#d8effd" else "#a7847d"
203 , className: classes.movingCloud3
204 , transform: "translate(0 " <> show (scrolled * 20.0) <> ")"
205 }
206 , mountains theme.isLight scrolled
207 --buildings
208 , buildings theme.isLight scrolled
209 -- bar
210 , SVG.path
211 { fill: theme.backgroundColour
212 , d: "M-3.028 431.298h826.844v20.006H-3.028z"
213 }
214 -- laptop
215 , laptop classes scrolled
216 ]
217 }
218 , defs theme.isLight
219 ]
220 }
222laptop ∷
223 ∀ r.
224 { cursor ∷ String
225 , keyword ∷ String
226 , laptopBackground ∷ String
227 , laptopBody ∷ String
228 , screenText ∷ String
229 , typeName ∷ String
230 | r
231 } ->
232 Number -> JSX
233laptop classes scrolled =
234 SVG.g
235 { children:
236 [ SVG.path
237 { d: "M606.064 273.603c0-6.261-5.076-11.337-11.337-11.337H399.673c-6.261 0-11.337 5.076-11.337 11.337v151.521a5.001 5.001 0 005 5h207.728a5.004 5.004 0 003.536-1.464 5.004 5.004 0 001.464-3.536V273.603z"
238 , fill: "#657390"
239 }
240 , SVG.path
241 { d: "M606.064 273.603c0-6.261-5.076-11.337-11.337-11.337H399.673c-6.261 0-11.337 5.076-11.337 11.337v151.521a5.001 5.001 0 005 5h207.728a5.004 5.004 0 003.536-1.464 5.004 5.004 0 001.464-3.536V273.603z"
242 , fill: "#657390"
243 }
244 , SVG.path
245 { d: "M599.193 275.905a5.528 5.528 0 00-5.526-5.526H400.604a5.528 5.528 0 00-5.526 5.526v141.039a5.528 5.528 0 005.526 5.526h193.063a5.528 5.528 0 005.526-5.526V275.905z"
246 , fill: "#130c2b"
247 , className: classes.laptopBackground
248 }
249 , SVG.path
250 { d: "M655.04 427.228a.458.458 0 00-.457-.457H340.83v4.975a5 5 0 005 5h304.21a5 5 0 005-5v-4.518z"
251 , className: classes.laptopBody
252 }
253 , SVG.circle
254 { cx: "497.178"
255 , cy: "267.001"
256 , r: "1.238"
257 , fillOpacity: ".45"
258 }
259 , SVG.circle
260 { cx: "500.009"
261 , cy: "267.001"
262 , r: ".527"
263 , fillOpacity: ".45"
264 }
265 , SVG.circle
266 { cx: "494.263"
267 , cy: "267.001"
268 , r: ".527"
269 , fill: "#102708"
270 , fillOpacity: ".45"
271 }
272 , SVG.text
273 { x: "407"
274 , y: "290"
275 , className: classes.screenText
276 , children:
277 textToTspans classes scrolled 407.0
278 $ intercalate "\n"
279 [ "hoistJoker "
280 , "\t∷ ∀ f g a b"
281 , "\t. (f ~> g)"
282 , "\t-> Joker f a b"
283 , "\t-> Joker g a b"
284 , "hoistJoker f (Joker a) ="
285 , "\t Joker (f a)"
286 ]
287 }
288 ]
289 }
291buildings ∷ Boolean -> Number -> JSX
292buildings isLight scrolled =
293 SVG.g
294 { transform: "translate(0 " <> show (scrolled * 30.0) <> ")"
295 , children:
296 [ SVG.path -- 2 main
297 { d: "M195.171 451.298V159.153H92.709v292.145h102.462z"
298 , fill: if isLight then "url(#_Linear4)" else "url(#_Linear5)"
299 }
300 , SVG.path -- 4 main
301 { d: "M289.755 451.298V232.811h-57.232v218.487h57.232z"
302 , fill: if isLight then "url(#_Linear5)" else "url(#_Linear6)"
303 }
304 , SVG.path -- 4 shade
305 { d: "M207.557 451.298V232.811h25.314v218.487h-25.314zM92.709 451.298V159.153h25.314v292.145H92.709z"
306 , fill: if isLight then "#b4dbfb" else "#130425"
307 }
308 , SVG.path -- 5 shade
309 { d: "M323.451 451.298V327.713h-71.555v123.585h71.555z"
310 , fill: if isLight then "url(#_Linear6)" else "url(#_Linear7)"
311 , transform: "translate(0 " <> show (scrolled * 60.0) <> ")"
312 }
313 , SVG.path -- 5 main
314 { d: "M323.451 451.298V327.713h-16.692v123.585h16.692z"
315 , fill: if isLight then "url(#_Linear7)" else "url(#_Linear8)"
316 , transform: "translate(0 " <> show (scrolled * 60.0) <> ")"
317 }
318 , SVG.path -- 3 main
319 { d: "M272.498 451.298v-95.771H135.943v95.771h136.555z"
320 , fill: if isLight then "url(#_Linear8)" else "url(#_Linear9)"
321 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
322 }
323 , SVG.path -- 3 shade
324 { d: "M173.526 451.298v-95.771h-37.583v95.771h37.583z"
325 , fill: if isLight then "#b4dbfb" else "#2e1232"
326 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
327 }
328 , SVG.path -- 1 main
329 { d: "M85.122 451.298V272.442h-97.675v178.856h97.675z"
330 , fill: if isLight then "#b4dbfb" else "url(#_Linear10)"
331 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
332 }
333 , SVG.path -- 1 shade
334 { d: "M66.649 456.944V272.442H-3.028v184.502h69.677z"
335 , fill: if isLight then "#b4dbfb" else "url(#_Linear11)"
336 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
337 }
338 ]
339 }
341mountains ∷ Boolean -> Number -> JSX
342mountains isLight scrolled =
343 SVG.g
344 { transform: "translate(0 " <> show (scrolled * 70.0) <> ")"
345 , children:
346 [ SVG.path
347 { d: "M476.098 218.299l81.644 216.765H352.334l123.764-216.765z"
348 , fill: if isLight then "#4560a0" else "#04102d"
349 }
350 , SVG.path
351 { d: "M476.098 218.299l21.823 203.477 81.177-8.706-103-194.771z"
352 , fill: if isLight then "#5e84ba" else "#250849"
353 }
354 -- 2 dark (left)
355 , SVG.path
356 { d: "M369.627 308.648l88.326 125.788H252.546l117.081-125.788z"
357 , fill: if isLight then "#4560a0" else "#04102d"
358 }
359 -- 1 dark (left)
360 , SVG.path
361 { d: "M326.11 338.332l68.193 96.732H188.896l137.214-96.732zM508.168 243.792l163.526 204.747H415.051l93.117-204.747z"
362 , fill: if isLight then "#4560a0" else "#04102d"
363 }
364 , SVG.path -- 1 and 2 light (right)
365 { d: "M508.168 245.355l63.395 75.115-54.953 46.379-8.442-121.494zM369.612 308.648l54.182 77.687-58.981 41.135 4.799-118.822zM326.184 338.326l16.813 23.99-16.887 11.835.074-35.825z"
366 , fill: if isLight then "#5e84ba" else "#250849"
367 }
368 -- right mountain
369 , SVG.path -- rightmost dark (left)
370 { d: "M633.647 230.42L809.34 435.064H457.953L633.647 230.42z"
371 , fill: if isLight then "#4560a0" else "#04102d"
372 }
373 , SVG.path -- rightmost light (right)
374 { d: "M633.647 230.42L735.22 475.549l86.041-35.653L633.647 230.42z"
375 , fill: if isLight then "#5e84ba" else "#250849"
376 }
377 , SVG.path -- peak left
378 { d: "M643.625 254.384c-.571.219-.745.283-1.863.82-2.864 1.395-6.079 3.209-9.184 2.64-6.187-1.134-10.854-6.283-16.725-6.738l17.786-20.729 9.986 24.007z"
379 , fill: if isLight then "#d9d9d9" else "#655578"
380 }
381 , SVG.path -- peak right
382 { d: "M654.229 253.4c-.494-.028-.875-.013-.875-.013-3.255-.486-6.617-.192-9.677.982l-.052.02-9.978-23.97 20.582 22.981z"
383 , fill: if isLight then "#fff" else "#9d779e"
384 }
385 ]
386 }
388textToTspans ∷
389 ∀ r.
390 { cursor ∷ String
391 , keyword ∷ String
392 , typeName ∷ String
393 | r
394 } ->
395 Number -> Number -> String -> Array JSX
396textToTspans classes scrolled x str = (foldl toTspan { distance: 0.0, acc: [] } lines).acc <> [ cursor ]
397 where
398 toTspan { distance, acc } line = case uncons (words line) of
399 Nothing -> { distance: distance + 1.2, acc }
400 Just { head: "", tail: [] } -> { distance: distance + 1.2, acc }
401 Just { head, tail } -> do
402 let
403 newElems ∷ Array JSX
404 newElems = _.spans $ foldl mkNewElems { prefix: " ", spans: [] } tail
406 mkNewElems { prefix, spans } = case _ of
407 "" -> { prefix: prefix <> " ", spans }
408 word ->
409 { prefix: " "
410 , spans:
411 spans
412 `snoc`
413 SVG.tspan
414 { children: [ HTML.text $ prefix <> word ]
415 , className: classNameForWord word
416 }
417 }
419 first =
420 SVG.tspan
421 { x: show x
422 , dy: show distance <> "em"
423 , children: [ HTML.text head ]
424 }
426 pimped ∷ Array JSX
427 pimped = first : newElems
428 { distance: 1.2, acc: acc <> pimped }
430 classNameForWord = case _ of
431 keyword
432 | elem keyword keywords -> classes.keyword
433 typeName
434 | isTypeName typeName -> classes.typeName
435 other -> ""
437 isTypeName s =
438 let
439 firstLetter = String.take 1 s
441 inRange c1 c2 = between (codePointFromChar c1) (codePointFromChar c2)
443 isLetter =
444 String.toCodePointArray
445 >>> head
446 >>> maybe false (inRange 'a' 'z' || inRange 'A' 'Z')
447 in
448 String.toUpper firstLetter == firstLetter && isLetter firstLetter
450 keywords =
451 [ "module"
452 , "∷"
453 , "::"
454 , "forall"
455 , "∀"
456 , "do"
457 , "where"
458 , "let"
459 , "in"
460 , "instance"
461 , "class"
462 , "data"
463 , "="
464 , "|"
465 , "type"
466 , "newtype"
467 , "derive"
468 , "if"
469 , "then"
470 , "else"
471 , "import"
472 ]
474 words line = split
475 where
476 split = String.split (String.Pattern " ") line
478 lines = String.split (String.Pattern "\n") scrolledString
480 scrolledString = String.take charactersToTake str
481 where
482 doneAfterXPercent = 0.5 -- [WARN] Don't set to 0
484 charactersToTake = round $ scrolled * (1.0 / doneAfterXPercent) * len
486 len = toNumber $ String.length str
488 cursor =
489 SVG.tspan
490 { dx: "0"
491 , dy: "0"
492 , children: [ HTML.text "█" ]
493 , className: classes.cursor
494 , id: "landing-page-cursor"
495 }
497defs ∷ Boolean -> JSX
498defs = if _ then lightDefs else darkDefs
500lightDefs ∷ JSX
501lightDefs =
502 SVG.defs
503 { children:
504 [ SVG.linearGradient
505 { id: "_Linear2"
506 , x1: "0"
507 , y1: "0"
508 , x2: "1"
509 , y2: "0"
510 , gradientUnits: "userSpaceOnUse"
511 , gradientTransform: "matrix(0 451.564 -803.856 0 398.9 -3.513)"
512 , children:
513 [ SVG.stop
514 { offset: "0"
515 , stopColor: "#68adfc"
516 }
517 , SVG.stop
518 { offset: ".34"
519 , stopColor: "#cbe0f8"
520 }
521 , SVG.stop
522 { offset: "1"
523 , stopColor: "#d2cbf8"
524 }
525 ]
526 }
527 , SVG.linearGradient
528 { id: "_Linear4"
529 , x1: "0"
530 , y1: "0"
531 , x2: "1"
532 , y2: "0"
533 , gradientUnits: "userSpaceOnUse"
534 , gradientTransform: "scale(293.8304) rotate(83.86 -.11 .484)"
535 , children:
536 [ SVG.stop
537 { offset: "0"
538 , stopColor: "#bde8f7"
539 }
540 , SVG.stop
541 { offset: "1"
542 , stopColor: "#86b6fe"
543 }
544 ]
545 }
546 , SVG.linearGradient
547 { id: "_Linear5"
548 , x1: "0"
549 , y1: "0"
550 , x2: "1"
551 , y2: "0"
552 , gradientUnits: "userSpaceOnUse"
553 , gradientTransform: "scale(219.3256) rotate(84.988 -.028 1.133)"
554 , children:
555 [ SVG.stop
556 { offset: "0"
557 , stopColor: "#bde8f7"
558 }
559 , SVG.stop
560 { offset: "1"
561 , stopColor: "#86b6fe"
562 }
563 ]
564 }
565 , SVG.linearGradient
566 { id: "_Linear6"
567 , x1: "0"
568 , y1: "0"
569 , x2: "1"
570 , y2: "0"
571 , gradientUnits: "userSpaceOnUse"
572 , gradientTransform: "rotate(90 -6.304 321.409) scale(123.585)"
573 , children:
574 [ SVG.stop
575 { offset: "0"
576 , stopColor: "#bde8f7"
577 }
578 , SVG.stop
579 { offset: "1"
580 , stopColor: "#86b6fe"
581 }
582 ]
583 }
584 , SVG.linearGradient
585 { id: "_Linear7"
586 , x1: "0"
587 , y1: "0"
588 , x2: "1"
589 , y2: "0"
590 , gradientUnits: "userSpaceOnUse"
591 , gradientTransform: "scale(132.4342) rotate(68.9 -1.112 2.415)"
592 , children:
593 [ SVG.stop
594 { offset: "0"
595 , stopColor: "#bde8f7"
596 }
597 , SVG.stop
598 { offset: "1"
599 , stopColor: "#86b6fe"
600 }
601 ]
602 }
603 , SVG.linearGradient
604 { id: "_Linear8"
605 , x1: "0"
606 , y1: "0"
607 , x2: "1"
608 , y2: "0"
609 , gradientUnits: "userSpaceOnUse"
610 , gradientTransform: "rotate(90 -88.788 166.635) scale(190.951)"
611 , children:
612 [ SVG.stop
613 { offset: "0"
614 , stopColor: "#bde8f7"
615 }
616 , SVG.stop
617 { offset: "1"
618 , stopColor: "#86b6fe"
619 }
620 ]
621 }
622 , SVG.linearGradient
623 { id: "_Linear9"
624 , x1: "0"
625 , y1: "0"
626 , x2: "1"
627 , y2: "0"
628 , gradientUnits: "userSpaceOnUse"
629 , gradientTransform: "matrix(314.21 0 0 9.97478 340.83 431.758)"
630 , children:
631 [ SVG.stop
632 { offset: "0"
633 , stopColor: "#a9abc1"
634 }
635 , SVG.stop
636 { offset: ".11"
637 , stopColor: "#65687d"
638 }
639 , SVG.stop
640 { offset: ".5"
641 , stopColor: "#c3c5d2"
642 }
643 , SVG.stop
644 { offset: ".89"
645 , stopColor: "#64677c"
646 }
647 , SVG.stop
648 { offset: "1"
649 , stopColor: "#a4a7c2"
650 }
651 ]
652 }
653 , SVG.radialGradient
654 { id: "_Radial3"
655 , cx: "0"
656 , cy: "0"
657 , r: "1"
658 , gradientUnits: "userSpaceOnUse"
659 , gradientTransform: "matrix(647.316 0 0 309.935 720.558 355.142)"
660 , children:
661 [ SVG.stop
662 { offset: "0"
663 , stopColor: "#cfcfcf"
664 , stopOpacity: ".65"
665 }
666 , SVG.stop
667 { offset: "1"
668 , stopColor: "#fff552"
669 , stopOpacity: "0"
670 }
671 ]
672 }
673 ]
674 }
676darkDefs ∷ JSX
677darkDefs =
678 SVG.defs
679 { children:
680 [ SVG.linearGradient
681 { id: "_Linear2"
682 , x1: "0"
683 , y1: "0"
684 , x2: "1"
685 , y2: "0"
686 , gradientUnits: "userSpaceOnUse"
687 , gradientTransform: "matrix(0 451.564 -803.856 0 398.9 -3.513)"
688 , children:
689 [ SVG.stop
690 { offset: "0"
691 , stopColor: "#000434"
692 }
693 , SVG.stop
694 { offset: ".28"
695 , stopColor: "#171043"
696 }
697 , SVG.stop
698 { offset: ".68"
699 , stopColor: "#573774"
700 }
701 , SVG.stop
702 { offset: "1"
703 , stopColor: "#b8734e"
704 }
705 ]
706 }
707 , SVG.linearGradient
708 { id: "_Linear4"
709 , x1: "0"
710 , y1: "0"
711 , x2: "1"
712 , y2: "0"
713 , gradientUnits: "userSpaceOnUse"
714 , gradientTransform: "matrix(-.53832 -17.3125 1.44013 -.04478 749.812 228.864)"
715 , children:
716 [ SVG.stop
717 { offset: "0"
718 , stopColor: "#e59f77"
719 }
720 , SVG.stop
721 { offset: ".47"
722 , stopColor: "#a46075"
723 }
724 , SVG.stop
725 { offset: "1"
726 , stopColor: "#914d74"
727 }
728 ]
729 }
730 , SVG.linearGradient
731 { id: "_Linear5"
732 , x1: "0"
733 , y1: "0"
734 , x2: "1"
735 , y2: "0"
736 , gradientUnits: "userSpaceOnUse"
737 , gradientTransform: "scale(293.8304) rotate(83.86 -.11 .484)"
738 , children:
739 [ SVG.stop
740 { offset: "0"
741 , stopColor: "#08001b"
742 }
743 , SVG.stop
744 { offset: "1"
745 , stopColor: "#571547"
746 }
747 ]
748 }
749 , SVG.linearGradient
750 { id: "_Linear6"
751 , x1: "0"
752 , y1: "0"
753 , x2: "1"
754 , y2: "0"
755 , gradientUnits: "userSpaceOnUse"
756 , gradientTransform: "scale(219.3256) rotate(84.988 -.028 1.133)"
757 , children:
758 [ SVG.stop
759 { offset: "0"
760 , stopColor: "#000418"
761 }
762 , SVG.stop
763 { offset: "1"
764 , stopColor: "#571548"
765 }
766 ]
767 }
768 , SVG.linearGradient
769 { id: "_Linear7"
770 , x1: "0"
771 , y1: "0"
772 , x2: "1"
773 , y2: "0"
774 , gradientUnits: "userSpaceOnUse"
775 , gradientTransform: "matrix(46.7869 123.585 -71.5547 27.0893 259.972 327.713)"
776 , children:
777 [ SVG.stop
778 { offset: "0"
779 , stopColor: "#000418"
780 }
781 , SVG.stop
782 { offset: "1"
783 , stopColor: "#571548"
784 }
785 ]
786 }
787 , SVG.linearGradient
788 { id: "_Linear8"
789 , x1: "0"
790 , y1: "0"
791 , x2: "1"
792 , y2: "0"
793 , gradientUnits: "userSpaceOnUse"
794 , gradientTransform: "rotate(90 -6.304 321.409) scale(123.585)"
795 , children:
796 [ SVG.stop
797 { offset: "0"
798 , stopColor: "#3a0843"
799 }
800 , SVG.stop
801 { offset: "1"
802 , stopColor: "#9f5c6a"
803 }
804 ]
805 }
806 , SVG.linearGradient
807 { id: "_Linear9"
808 , x1: "0"
809 , y1: "0"
810 , x2: "1"
811 , y2: "0"
812 , gradientUnits: "userSpaceOnUse"
813 , gradientTransform: "scale(101.643) rotate(28.107 -6.187 4.945)"
814 , children:
815 [ SVG.stop
816 { offset: "0"
817 , stopColor: "#2c1d2f"
818 }
819 , SVG.stop
820 { offset: "1"
821 , stopColor: "#480445"
822 }
823 ]
824 }
825 , SVG.linearGradient
826 { id: "_Linear10"
827 , x1: "0"
828 , y1: "0"
829 , x2: "1"
830 , y2: "0"
831 , gradientUnits: "userSpaceOnUse"
832 , gradientTransform: "scale(-106.68957) rotate(-86.136 -1.645 -.807)"
833 , children:
834 [ SVG.stop
835 { offset: "0"
836 , stopColor: "#2c1d2f"
837 }
838 , SVG.stop
839 { offset: "1"
840 , stopColor: "#480445"
841 }
842 ]
843 }
844 , SVG.linearGradient
845 { id: "_Linear11"
846 , x1: "0"
847 , y1: "0"
848 , x2: "1"
849 , y2: "0"
850 , gradientUnits: "userSpaceOnUse"
851 , gradientTransform: "matrix(20.5602 0 -184.503 0 11.25 272.442)"
852 , children:
853 [ SVG.stop
854 { offset: "0"
855 , stopColor: "#000624"
856 }
857 , SVG.stop
858 { offset: "1"
859 , stopColor: "#4f1557"
860 }
861 ]
862 }
863 , SVG.linearGradient
864 { id: "_Linear12"
865 , x1: "0"
866 , y1: "0"
867 , x2: "1"
868 , y2: "0"
869 , gradientUnits: "userSpaceOnUse"
870 , gradientTransform: "matrix(314.21 0 0 9.97478 340.83 431.758)"
871 , children:
872 [ SVG.stop
873 { offset: "0"
874 , stopColor: "#a9abc1"
875 }
876 , SVG.stop
877 { offset: ".11"
878 , stopColor: "#65687d"
879 }
880 , SVG.stop
881 { offset: ".5"
882 , stopColor: "#c3c5d2"
883 }
884 , SVG.stop
885 { offset: ".89"
886 , stopColor: "#64677c"
887 }
888 , SVG.stop
889 { offset: "1"
890 , stopColor: "#a4a7c2"
891 }
892 ]
893 }
894 , SVG.radialGradient
895 { id: "_Radial3"
896 , cx: "0"
897 , cy: "0"
898 , r: "1"
899 , gradientUnits: "userSpaceOnUse"
900 , gradientTransform: "matrix(330.479 0 0 252.189 174.599 310.24)"
901 , children:
902 [ SVG.stop
903 { offset: "0"
904 , stopColor: "#f60"
905 , stopOpacity: ".3"
906 }
907 , SVG.stop
908 { offset: "1"
909 , stopColor: "#fff552"
910 , stopOpacity: "0"
911 }
912 ]
913 }
914 ]
915 }
917infixr 8 zip as <%>
919stars ∷ ∀ css. String -> { twinklingStar1 ∷ String, twinklingStar2 ∷ String, twinklingStar3 ∷ String | css } -> JSX
920stars className classes =
921 let
922 n = 400
924 xs = randomRs 0.0 800.0 n $ mkSeed 84
926 ys = randomRs (-20.0) 200.0 n $ mkSeed 4398
928 radiuses = randomRs 0.1 1.3 n (mkSeed 15)
930 opacities = randomRs 0.5 0.9 n (mkSeed 92)
932 classNames =
933 randomRs 1 50 n (mkSeed 3)
934 <#> case _ of
935 1 -> classes.twinklingStar1
936 2 -> classes.twinklingStar2
937 3 -> classes.twinklingStar2
938 _ -> ""
940 zipped = xs <%> ys <%> radiuses <%> classNames <%> opacities
942 children = do
943 cx /\ cy /\ r /\ className' /\ opacity <- zipped
944 pure
945 $ SVG.circle
946 { cx: show cx
947 , cy: show cy
948 , r: show r
949 , fill: "#eeefff"
950 , className: className'
951 , opacity: show (opacity - cy / 200.0)
952 }
953 in
954 SVG.g
955 { fillOpacity: ".67"
956 , children
957 , className
958 }