tfrere HF Staff Cursor commited on
Commit
96f54de
·
1 Parent(s): 6cc4cc6

feat: redesign welcome modal with keycap control grid

Browse files

Rebuild the welcome card for at-a-glance clarity: typographic identity
under the duck-head mark, a one-line value proposition, six keycap
tiles on an aligned grid (move cluster, kick A/E, roll, ball, camera,
reset), and a compact gamepad/ghosts row. Refined entry animation with
a gentle row stagger (reduced-motion aware), layered depth, and a
compact two-column layout on narrow screens; the card scrolls only as
a last resort on very short viewports.

Co-authored-by: Cursor <cursoragent@cursor.com>

Files changed (1) hide show
  1. index.html +262 -66
index.html CHANGED
@@ -253,83 +253,203 @@
253
  inset: 0;
254
  z-index: 30;
255
  display: flex;
256
- align-items: center;
257
- justify-content: center;
258
- padding: 1.5rem;
259
- background: rgba(8, 8, 12, 0.45);
260
- backdrop-filter: blur(16px) saturate(1.3);
261
- -webkit-backdrop-filter: blur(16px) saturate(1.3);
262
  opacity: 1;
263
  transition: opacity 0.35s ease;
264
  }
265
  #welcome.closing { opacity: 0; pointer-events: none; }
266
  #welcome .card {
267
  position: relative;
268
- max-width: 430px;
 
269
  width: 100%;
270
- padding: 2.2rem 2.2rem 2rem;
271
- border: 1px solid rgba(255, 255, 255, 0.12);
272
- border-radius: 20px;
 
273
  background:
274
- radial-gradient(120% 90% at 50% 0%, rgba(255, 210, 63, 0.07), transparent 55%),
275
- rgba(14, 14, 20, 0.92);
276
- box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
 
 
 
277
  text-align: center;
278
- animation: card-in 0.5s cubic-bezier(0.2, 0.9, 0.25, 1.15) both;
279
  }
280
  @keyframes card-in {
281
- from { transform: translateY(18px) scale(0.94); opacity: 0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  to { transform: none; opacity: 1; }
283
  }
284
- /* Duck-head mark from the site header: closed by default, open on
285
- hover (same swap as the site). */
 
 
 
286
  #welcome .logo-head {
287
  position: relative;
288
- width: 7.2rem;
289
- margin: 0.3rem auto 0;
290
  filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.45));
291
  }
292
  #welcome .logo-head img { display: block; width: 100%; }
293
  #welcome .logo-head .open { position: absolute; inset: 0; opacity: 0; }
294
  #welcome .logo-head:hover .open { opacity: 1; }
295
  #welcome .logo-head:hover .closed { opacity: 0; }
 
 
 
 
 
 
 
296
  #welcome .subtitle {
297
- margin-top: 0.55rem;
298
- font-size: 0.62rem;
299
  font-weight: 700;
300
- letter-spacing: 0.32em;
 
301
  text-transform: uppercase;
302
- color: rgba(255, 210, 63, 0.75);
303
  }
304
  #welcome .tagline {
305
- margin-top: 0.5rem;
306
- font-size: 0.8rem;
307
- line-height: 1.65;
308
- color: rgba(255, 255, 255, 0.6);
 
 
309
  }
310
- #welcome ul {
311
- list-style: none;
312
- margin: 1.4rem 0 1.6rem;
313
- text-align: left;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  display: flex;
315
  flex-direction: column;
316
- gap: 0.55rem;
 
 
 
317
  }
318
- #welcome li {
319
  display: flex;
320
- align-items: baseline;
321
- gap: 0.7rem;
322
- font-size: 0.78rem;
323
- line-height: 1.5;
324
- color: rgba(255, 255, 255, 0.72);
325
  }
326
- #welcome li b {
327
- flex: 0 0 4.6rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  font-size: 0.62rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  font-weight: 700;
330
- letter-spacing: 0.16em;
 
331
  text-transform: uppercase;
332
- color: rgba(255, 210, 63, 0.85);
 
 
 
 
 
 
 
 
 
333
  }
334
  #welcome-go {
335
  appearance: none;
@@ -340,22 +460,54 @@
340
  font-size: 0.75rem;
341
  font-weight: 700;
342
  letter-spacing: 0.14em;
 
343
  text-transform: uppercase;
344
- padding: 0.75rem 2.2rem;
345
  border-radius: 999px;
346
  cursor: pointer;
347
  transition: transform 0.15s ease, box-shadow 0.15s ease;
348
  }
349
  #welcome-go:hover {
350
- transform: translateY(-1px) scale(1.03);
351
- box-shadow: 0 6px 24px rgba(255, 210, 63, 0.35);
 
 
 
 
352
  }
353
  #welcome .fineprint {
354
  margin-top: 0.9rem;
355
- font-size: 0.6rem;
356
  letter-spacing: 0.1em;
357
  text-transform: uppercase;
358
- color: rgba(255, 255, 255, 0.3);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
360
  #brand { cursor: pointer; }
361
  </style>
@@ -410,26 +562,70 @@
410
 
411
  <div id="welcome" hidden>
412
  <div class="card" role="dialog" aria-modal="true" aria-label="Microduck playground">
413
- <div class="logo-head">
414
- <img class="closed" id="logo-head-closed" alt="Microduck" />
415
- <img class="open" id="logo-head-open" alt="" aria-hidden="true" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  </div>
417
- <div class="subtitle">RL playground</div>
418
- <p class="tagline">
419
- A real robot brain in your browser: MuJoCo physics compiled to
420
- WebAssembly, driven by the duck's actual trained neural policies
421
- at 50&nbsp;Hz. No server, no pre-baked animation.
422
- </p>
423
- <ul>
424
- <li><b>Move</b><span>Arrows or ZQSD to run and turn, drag to orbit.</span></li>
425
- <li><b>Roll</b><span>R triggers a barrel roll, then it gets back up.</span></li>
426
- <li><b>Kick</b><span>A / E kick left / right (LB / RB on the pad).</span></li>
427
- <li><b>Ball</b><span>B pops a ball to kick around (Y on the pad).</span></li>
428
- <li><b>Camera</b><span>Follows from behind; drag to break free, C re-attaches.</span></li>
429
- <li><b>Gamepad</b><span>Plug one in - same mapping as the real robot.</span></li>
430
- <li><b>Ghosts</b><span>Other visitors right now appear as translucent ducks, live.</span></li>
431
- </ul>
432
- <button id="welcome-go">Waddle in</button>
433
  <div class="fineprint">click the title anytime to see this again</div>
434
  </div>
435
  </div>
 
253
  inset: 0;
254
  z-index: 30;
255
  display: flex;
256
+ padding: 1.25rem;
257
+ overflow-y: auto;
258
+ background: rgba(8, 8, 12, 0.5);
259
+ backdrop-filter: blur(18px) saturate(1.3);
260
+ -webkit-backdrop-filter: blur(18px) saturate(1.3);
 
261
  opacity: 1;
262
  transition: opacity 0.35s ease;
263
  }
264
  #welcome.closing { opacity: 0; pointer-events: none; }
265
  #welcome .card {
266
  position: relative;
267
+ /* Auto margins center the card and degrade to scroll on overflow */
268
+ margin: auto;
269
  width: 100%;
270
+ max-width: 480px;
271
+ padding: 2.3rem 2.4rem 1.8rem;
272
+ border: 1px solid rgba(255, 255, 255, 0.1);
273
+ border-radius: 24px;
274
  background:
275
+ radial-gradient(130% 80% at 50% 0%, rgba(255, 210, 63, 0.06), transparent 55%),
276
+ rgba(13, 13, 19, 0.94);
277
+ box-shadow:
278
+ inset 0 1px 0 rgba(255, 255, 255, 0.06),
279
+ 0 24px 60px rgba(0, 0, 0, 0.55),
280
+ 0 80px 160px rgba(0, 0, 0, 0.4);
281
  text-align: center;
282
+ animation: card-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
283
  }
284
  @keyframes card-in {
285
+ from { transform: translateY(14px) scale(0.965); opacity: 0; }
286
+ to { transform: none; opacity: 1; }
287
+ }
288
+ /* Rows fade in with a gentle stagger; no bounce anywhere. */
289
+ #welcome .card > * {
290
+ animation: row-in 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
291
+ }
292
+ #welcome .card > :nth-child(1) { animation-delay: 0.05s; }
293
+ #welcome .card > :nth-child(2) { animation-delay: 0.11s; }
294
+ #welcome .card > :nth-child(3) { animation-delay: 0.17s; }
295
+ #welcome .card > :nth-child(4) { animation-delay: 0.23s; }
296
+ #welcome .card > :nth-child(5) { animation-delay: 0.27s; }
297
+ @keyframes row-in {
298
+ from { transform: translateY(8px); opacity: 0; }
299
  to { transform: none; opacity: 1; }
300
  }
301
+ @media (prefers-reduced-motion: reduce) {
302
+ #welcome .card, #welcome .card > * { animation: none; }
303
+ }
304
+ /* Header: duck-head mark (closed by default, open on hover - same
305
+ swap as the site) over a clean typographic identity. */
306
  #welcome .logo-head {
307
  position: relative;
308
+ width: 5.8rem;
309
+ margin: 0 auto;
310
  filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.45));
311
  }
312
  #welcome .logo-head img { display: block; width: 100%; }
313
  #welcome .logo-head .open { position: absolute; inset: 0; opacity: 0; }
314
  #welcome .logo-head:hover .open { opacity: 1; }
315
  #welcome .logo-head:hover .closed { opacity: 0; }
316
+ #welcome .title {
317
+ margin-top: 0.8rem;
318
+ font-size: 1.4rem;
319
+ font-weight: 700;
320
+ letter-spacing: -0.01em;
321
+ color: rgba(255, 255, 255, 0.95);
322
+ }
323
  #welcome .subtitle {
324
+ margin-top: 0.35rem;
325
+ font-size: 0.6rem;
326
  font-weight: 700;
327
+ letter-spacing: 0.34em;
328
+ text-indent: 0.34em; /* recenter: tracking pads the right edge only */
329
  text-transform: uppercase;
330
+ color: rgba(255, 210, 63, 0.8);
331
  }
332
  #welcome .tagline {
333
+ margin: 0.8rem auto 0;
334
+ max-width: 42ch;
335
+ font-size: 0.78rem;
336
+ line-height: 1.6;
337
+ color: rgba(255, 255, 255, 0.58);
338
+ text-wrap: balance;
339
  }
340
+ /* Section label with hairlines on both sides */
341
+ #welcome .section-label {
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 0.9rem;
345
+ margin: 1.5rem 0 0.8rem;
346
+ font-size: 0.56rem;
347
+ font-weight: 700;
348
+ letter-spacing: 0.28em;
349
+ text-indent: 0.28em;
350
+ text-transform: uppercase;
351
+ color: rgba(255, 255, 255, 0.35);
352
+ }
353
+ #welcome .section-label::before,
354
+ #welcome .section-label::after {
355
+ content: "";
356
+ flex: 1;
357
+ height: 1px;
358
+ background: rgba(255, 255, 255, 0.08);
359
+ }
360
+ /* Controls: six keycap tiles on a tidy grid */
361
+ #welcome .grid {
362
+ display: grid;
363
+ grid-template-columns: repeat(3, 1fr);
364
+ gap: 0.5rem;
365
+ }
366
+ #welcome .tile {
367
+ padding: 0.75rem 0.4rem 0.7rem;
368
+ border: 1px solid rgba(255, 255, 255, 0.07);
369
+ border-radius: 14px;
370
+ background: rgba(255, 255, 255, 0.03);
371
+ }
372
+ /* Fixed-height keycap area so every tile's label sits on the same
373
+ baseline whether it holds one cap or the arrow cluster. */
374
+ #welcome .caps {
375
  display: flex;
376
  flex-direction: column;
377
+ align-items: center;
378
+ justify-content: center;
379
+ gap: 0.2rem;
380
+ height: 3.3rem;
381
  }
382
+ #welcome .caps .krow {
383
  display: flex;
384
+ gap: 0.2rem;
385
+ justify-content: center;
 
 
 
386
  }
387
+ #welcome kbd {
388
+ display: inline-flex;
389
+ align-items: center;
390
+ justify-content: center;
391
+ min-width: 1.55rem;
392
+ height: 1.55rem;
393
+ padding: 0 0.45rem;
394
+ font: inherit;
395
+ font-size: 0.68rem;
396
+ font-weight: 600;
397
+ color: rgba(255, 255, 255, 0.88);
398
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.04));
399
+ border: 1px solid rgba(255, 255, 255, 0.16);
400
+ border-radius: 6px;
401
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.08);
402
+ }
403
+ /* The arrow cluster packs six caps in two rows, so its caps shrink a
404
+ notch; every other tile keeps the full-size cap. */
405
+ #welcome .caps.cluster kbd { min-width: 1.4rem; height: 1.4rem; padding: 0 0.3rem; font-size: 0.62rem; }
406
+ #welcome .t-name {
407
+ margin-top: 0.55rem;
408
+ font-size: 0.62rem;
409
+ font-weight: 700;
410
+ letter-spacing: 0.18em;
411
+ text-indent: 0.18em;
412
+ text-transform: uppercase;
413
+ color: rgba(255, 255, 255, 0.85);
414
+ }
415
+ #welcome .t-hint {
416
+ margin-top: 0.2rem;
417
+ font-size: 0.6rem;
418
+ color: rgba(255, 255, 255, 0.38);
419
+ }
420
+ #welcome .orbit-hint {
421
+ margin-top: 0.7rem;
422
  font-size: 0.62rem;
423
+ letter-spacing: 0.08em;
424
+ color: rgba(255, 255, 255, 0.35);
425
+ }
426
+ /* Secondary row: gamepad + live ghosts */
427
+ #welcome .extras {
428
+ display: grid;
429
+ grid-template-columns: 1fr 1fr;
430
+ gap: 1.2rem;
431
+ margin-top: 1.3rem;
432
+ padding-top: 1.1rem;
433
+ border-top: 1px solid rgba(255, 255, 255, 0.07);
434
+ }
435
+ #welcome .x-label {
436
+ display: block;
437
+ margin-bottom: 0.3rem;
438
+ font-size: 0.56rem;
439
  font-weight: 700;
440
+ letter-spacing: 0.24em;
441
+ text-indent: 0.24em;
442
  text-transform: uppercase;
443
+ color: rgba(255, 210, 63, 0.7);
444
+ }
445
+ #welcome .x-item {
446
+ font-size: 0.68rem;
447
+ line-height: 1.5;
448
+ color: rgba(255, 255, 255, 0.5);
449
+ text-wrap: balance;
450
+ }
451
+ #welcome .cta {
452
+ margin-top: 1.5rem;
453
  }
454
  #welcome-go {
455
  appearance: none;
 
460
  font-size: 0.75rem;
461
  font-weight: 700;
462
  letter-spacing: 0.14em;
463
+ text-indent: 0.14em;
464
  text-transform: uppercase;
465
+ padding: 0.8rem 2.6rem;
466
  border-radius: 999px;
467
  cursor: pointer;
468
  transition: transform 0.15s ease, box-shadow 0.15s ease;
469
  }
470
  #welcome-go:hover {
471
+ transform: translateY(-1px);
472
+ box-shadow: 0 8px 28px rgba(255, 210, 63, 0.3);
473
+ }
474
+ #welcome-go:focus-visible {
475
+ outline: 2px solid rgba(255, 255, 255, 0.85);
476
+ outline-offset: 3px;
477
  }
478
  #welcome .fineprint {
479
  margin-top: 0.9rem;
480
+ font-size: 0.58rem;
481
  letter-spacing: 0.1em;
482
  text-transform: uppercase;
483
+ color: rgba(255, 255, 255, 0.28);
484
+ }
485
+ /* Narrow screens: stack the grid two-up, tighten the whole rhythm so
486
+ the card still fits a small phone viewport without scrolling */
487
+ @media (max-width: 480px) {
488
+ #welcome .card { padding: 1.6rem 1.3rem 1.4rem; border-radius: 20px; }
489
+ #welcome .logo-head { width: 4.3rem; }
490
+ #welcome .title { margin-top: 0.6rem; font-size: 1.2rem; }
491
+ #welcome .tagline { margin-top: 0.55rem; font-size: 0.73rem; }
492
+ #welcome .section-label { margin: 1.1rem 0 0.7rem; }
493
+ #welcome .grid { grid-template-columns: repeat(2, 1fr); gap: 0.45rem; }
494
+ #welcome .tile { padding: 0.6rem 0.4rem 0.55rem; }
495
+ #welcome .caps { height: 2.7rem; }
496
+ #welcome .t-name { margin-top: 0.4rem; }
497
+ #welcome .orbit-hint { margin-top: 0.55rem; }
498
+ #welcome .extras { grid-template-columns: 1fr; gap: 0.5rem; margin-top: 1rem; padding-top: 0.9rem; }
499
+ #welcome .x-label { display: inline; margin-right: 0.45rem; }
500
+ #welcome .cta { margin-top: 1.1rem; }
501
+ }
502
+ /* Short laptop viewports: shave vertical rhythm so it never scrolls */
503
+ @media (max-height: 760px) {
504
+ #welcome .logo-head { width: 4.8rem; }
505
+ #welcome .card { padding-top: 1.7rem; }
506
+ #welcome .title { margin-top: 0.6rem; font-size: 1.25rem; }
507
+ #welcome .tagline { margin-top: 0.6rem; }
508
+ #welcome .section-label { margin: 1.1rem 0 0.7rem; }
509
+ #welcome .extras { margin-top: 1rem; padding-top: 0.9rem; }
510
+ #welcome .cta { margin-top: 1.1rem; }
511
  }
512
  #brand { cursor: pointer; }
513
  </style>
 
562
 
563
  <div id="welcome" hidden>
564
  <div class="card" role="dialog" aria-modal="true" aria-label="Microduck playground">
565
+ <header>
566
+ <div class="logo-head">
567
+ <img class="closed" id="logo-head-closed" alt="Microduck" />
568
+ <img class="open" id="logo-head-open" alt="" aria-hidden="true" />
569
+ </div>
570
+ <h1 class="title">Microduck</h1>
571
+ <div class="subtitle">RL playground</div>
572
+ <p class="tagline">
573
+ Real physics, real trained neural policies, running live in
574
+ your browser fifty times a second.
575
+ </p>
576
+ </header>
577
+ <section aria-label="Controls">
578
+ <div class="section-label">Controls</div>
579
+ <div class="grid">
580
+ <div class="tile">
581
+ <div class="caps cluster">
582
+ <span class="krow"><kbd>&uarr;</kbd></span>
583
+ <span class="krow"><kbd>&larr;</kbd><kbd>&darr;</kbd><kbd>&rarr;</kbd></span>
584
+ </div>
585
+ <div class="t-name">Move</div>
586
+ <div class="t-hint">arrows or ZQSD</div>
587
+ </div>
588
+ <div class="tile">
589
+ <div class="caps"><span class="krow"><kbd>A</kbd><kbd>E</kbd></span></div>
590
+ <div class="t-name">Kick</div>
591
+ <div class="t-hint">left / right</div>
592
+ </div>
593
+ <div class="tile">
594
+ <div class="caps"><kbd>R</kbd></div>
595
+ <div class="t-name">Roll</div>
596
+ <div class="t-hint">barrel roll</div>
597
+ </div>
598
+ <div class="tile">
599
+ <div class="caps"><kbd>B</kbd></div>
600
+ <div class="t-name">Ball</div>
601
+ <div class="t-hint">pop one in</div>
602
+ </div>
603
+ <div class="tile">
604
+ <div class="caps"><kbd>C</kbd></div>
605
+ <div class="t-name">Camera</div>
606
+ <div class="t-hint">toggle chase</div>
607
+ </div>
608
+ <div class="tile">
609
+ <div class="caps"><kbd>Space</kbd></div>
610
+ <div class="t-name">Reset</div>
611
+ <div class="t-hint">fresh start</div>
612
+ </div>
613
+ </div>
614
+ <div class="orbit-hint">drag to orbit &middot; scroll to zoom</div>
615
+ </section>
616
+ <section class="extras">
617
+ <div class="x-item">
618
+ <span class="x-label">Gamepad</span>
619
+ Plug one in, same mapping as the real robot.
620
+ </div>
621
+ <div class="x-item">
622
+ <span class="x-label">Live ghosts</span>
623
+ Other visitors appear as translucent ducks.
624
+ </div>
625
+ </section>
626
+ <div class="cta">
627
+ <button id="welcome-go">Waddle in</button>
628
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  <div class="fineprint">click the title anytime to see this again</div>
630
  </div>
631
  </div>