Skip to content →

The Toybox

Widgets are the decorations and toys, the cat, the snow, the counters. They are JavaScript, individually loadable, and strictly optional: a retrostrap page with zero widgets is still a retrostrap page. Load the core script and the toybox, then declare what you want on any element:

index.html
<script defer src="https://cdn.jsdelivr.net/npm/retrostrap@0.1.0/dist/retrostrap.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/retrostrap@0.1.0/dist/retrostrap-toybox.min.js"></script>

<body data-rs-widgets="snowfall neko"
      data-rs-neko-behavior="patrol" data-rs-neko-target="#masthead">

Every widget honors the ten-point Toybox contract: budgeted, pausable when the tab is hidden, invisible to assistive tech, and silent under prefers-reduced-motion if it's decorative. There are no live specimens on this page, a page full of snow and cats would bury the documentation. Load the toybox and try them on a page of your own.


clock: Clock

The JavaScript clock tucked into the corner of the 'enhanced' homepage.

An LED clock, refreshed once a second.

informative

Options: data-rs-clock-<name>
OptionTypeDefaultNotes
format 12 · 24 24
seconds bool true
label string - a caption under the digits

Accessibility: The visible digits are aria-hidden; a screen reader reads an sr-only <time>, updated once a minute, not every tick. A changing digit is content, not motion, so reduced motion changes nothing.

example
<span data-rs-widgets="clock" data-rs-clock-format="12" data-rs-clock-label="server time"></span>

↑ top


countdown: Countdown

"Only 47 days until the year 2000!" on a million homepages.

An LED countdown to a target date and time.

informative

Options: data-rs-countdown-<name>
OptionTypeDefaultNotes
to datetime - ISO datetime; required
done string - message shown at zero

Events: rs:countdown:done

Accessibility: The digits are aria-hidden; a screen reader hears an sr-only remaining-time line updated once a minute. A changing digit is content, so reduced motion keeps the count.

example
<span data-rs-widgets="countdown" data-rs-countdown-to="2000-01-01T00:00:00Z"></span>

↑ top


crt: CRT overlay

The look of a page seen through a tube monitor at 2 a.m.

Scanlines and a vignette, with an optional (off-by-default) flicker.

decorative

Options: data-rs-crt-<name>
OptionTypeDefaultNotes
vignette bool true
flicker bool false capped at 2 changes/sec, tiny opacity delta; force-disabled under reduced motion

Accessibility: Decoration: aria-hidden, pointer-events:none. Flicker stays far below the photosensitivity threshold and never runs under prefers-reduced-motion.

example
<body data-rs-widgets="crt">

↑ top


cursor-trail: Cursor trail

The DHTML trail scripts and Comet Cursor of the late-90s 'enhanced' homepage.

A little pool of sprites that chase your pointer.

decorative pointer only

Options: data-rs-cursor-trail-<name>
OptionTypeDefaultNotes
variant dots · stars · sparkle · hearts · bubbles · image stars
count int 8 max 16
size px 12 max 24
color color - legal palette; default theme accent
image url - for variant=image

Accessibility: Decoration: the layer is aria-hidden and pointer-events:none. Never spawns under prefers-reduced-motion, and no-ops on touch.

example
<!-- add the toybox script, then declare the widget on any element -->
<body data-rs-widgets="cursor-trail" data-rs-cursor-trail-variant="stars" data-rs-cursor-trail-count="10">

↑ top


dvd: Bouncing logo

The DVD screensaver logo that hypnotized a generation.

A logo that bounces around the viewport, waiting for the corner hit.

decorative

Options: data-rs-dvd-<name>
OptionTypeDefaultNotes
target selector - an element to bounce; otherwise a default chip
text string - text for the default chip
speed slow · normal · fast normal

Events: rs:dvd:corner

Accessibility: Decoration: aria-hidden, pointer-events:none. Under prefers-reduced-motion it parks in a corner and holds still.

example
<body data-rs-widgets="dvd">

↑ top


fortune: Fortune floppy

The fortune cookie of the command line, pinned to a homepage.

Shows an old-web fortune; click for another.

informative

Options: data-rs-fortune-<name>
OptionTypeDefaultNotes
list json-array - custom fortunes via an inline application/json block, not a data attribute

Events: rs:fortune:next

Accessibility: Plain text and a real button. The pick is seeded off the clock, so no Math.random.

example
<div class="rs-panel" data-rs-widgets="fortune"></div>

↑ top


guestbook: Guestbook

The comment section's gentle ancestor: a form, a list, and social obligation.

Renders guestbook entries and wires the sign form.

informative

Options: data-rs-guestbook-<name>
OptionTypeDefaultNotes
src url - endpoint or JSON file; or an inline <script type=application/json>

Events: rs:content

Accessibility: Every entry renders as text nodes only, injected markup shows as literal text, never runs. A missing endpoint shows a friendly note. Honeypot and minimum-time traps guard an existing form.

example
<section data-rs-widgets="guestbook" data-rs-guestbook-src="guestbook.json">
  <form action="mailto:me@example.com" method="post">…the rs- form fields…</form>
</section>

↑ top


hit-counter: Hit counter

The CGI counter that proved your page had visitors, in a pre-analytics world.

The odometer visitor counter: static, local, or api-backed.

informative

Options: data-rs-hit-counter-<name>
OptionTypeDefaultNotes
mode static · local · api static
value int 1337 for static mode
src url - for api mode; POST returns { count }
digits int 6 zero-pad width; the label line is your own rs-counter__label markup

Accessibility: Announced once to a screen reader as ordinary text, never on a tick. The api contract counts hits, never people, no identifiers stored (Decency Law).

example
You are visitor
<span data-rs-widgets="hit-counter" data-rs-hit-counter-mode="local"></span>

↑ top


jukebox: Jukebox

The MIDI player tucked in the corner, but this one waits for your click.

A playlist audio player in window chrome. Never autoplays.

informative

Options: data-rs-jukebox-<name>
OptionTypeDefaultNotes
tracks json-array - each { title, src }; attribute JSON or an inline JSON block
loop bool false
src url - a fallback audio URL: plays when a track has no src, or alone as a one-track player; never fetched as a playlist

Events: rs:jukebox:track

Accessibility: NEVER autoplays, there is no autoplay option (Decency Law). Fully keyboard-operable; the fake EQ is aria-hidden and holds still under reduced motion.

example
<div data-rs-widgets="jukebox">
  <script type="application/json">{"tracks":[{"title":"Hangar Bay","src":"audio/hangar.ogg"}]}</script>
</div>

↑ top


konami: Konami code

Up up down down left right left right B A: the most-typed secret on Earth.

The cheat code, and a 30-second party mode.

decorative

Options: data-rs-konami-<name>
OptionTypeDefaultNotes
href url - navigate to a secret page on the code, instead of party mode
words string - comma-separated secret words that also trigger the egg when typed

Accessibility: Loading the widget adds Retrostrap.konami.on(seq, fn) and .trigger(). Party mode composes only non-flashing widgets and stays in budget; under reduced motion it declines with a small note. A ten-tap gesture is the touch alternative.

example
<body data-rs-widgets="konami">
<!-- or give it a secret page: data-rs-konami-href="secret.html" -->

↑ top


kugeln: Kugeln

The spinning-ball DHTML cursor toys of around 2000, coloured orbs that chased the mouse across a homepage.

Shiny 3D spheres that trail or orbit your pointer, in a rainbow of colours.

decorative pointer only

Options: data-rs-kugeln-<name>
OptionTypeDefaultNotes
colors rainbow · red · gold · green · cyan · blue · purple · pink · silver rainbow
count int 7 max 12
size px 20 8-32
mode trail · orbit trail

Accessibility: Decoration: the layer is aria-hidden and pointer-events:none. Never spawns under prefers-reduced-motion, and no-ops on touch.

example
<!-- add the toybox script, then declare the widget on any element -->
<!-- colors: rainbow (default) or one of red gold green cyan blue purple pink silver -->
<body data-rs-widgets="kugeln" data-rs-kugeln-colors="rainbow" data-rs-kugeln-count="7" data-rs-kugeln-mode="trail">

↑ top


last-updated: Last updated

"Last updated: 04/17/1998", the proudest line on any homepage.

Prints the document's last-modified date, era-style.

informative

Options: data-rs-last-updated-<name>
OptionTypeDefaultNotes
prefix string Last updated:
date string - pin this date verbatim instead of the file's last-modified time
format long · short long
locale string - defaults to the page lang

Accessibility: Static text; nothing to rule on. Hides itself if the browser reports a garbage date.

example
<p class="rs-footer__updated" data-rs-widgets="last-updated"></p>

↑ top


neko: Neko (Gif the cat)

The desktop-cat lineage (Neko/oneko), on the web since the late 90s. Gif is our own, drawn from a blank grid.

A pixel cat that chases your cursor, patrols an element edge, or dozes.

decorative

Options: data-rs-neko-<name>
OptionTypeDefaultNotes
skin gif · calico · void gif
behavior chase · patrol · sleepy chase
target selector - the element to patrol the top edge of
speed slow · normal · fast normal
src string - override the sprite sheet URL (defaults to the skin's)

Events: rs:neko:sleep, rs:neko:wake

Accessibility: Decoration: aria-hidden, pointer-events:none. Under prefers-reduced-motion the cat stays, sitting still. Chase falls back to patrol on touch. Two cats per page, maximum.

example
<!-- Gif patrols the top edge of the masthead -->
<body data-rs-widgets="neko" data-rs-neko-skin="gif"
      data-rs-neko-behavior="patrol" data-rs-neko-target="#masthead">

↑ top


pixel-pet: Pixel pet

The virtual pets of the early-2000s web, a creature that lived in the corner of your page.

A little desktop pet in the corner that you feed, and that remembers you between visits.

informative

Options: data-rs-pixel-pet-<name>
OptionTypeDefaultNotes
name string Blobby
color green · blue · pink · gold green
pace int - seconds until fully hungry; default 300

Events: rs:pixel-pet:fed, rs:pixel-pet:gold

Accessibility: The pet is a labelled button you can feed with the keyboard; its mood is shown as text. State persists in localStorage. Appears (static) under prefers-reduced-motion.

example
<!-- add the toybox script, then declare the pet anywhere; it lives in a corner -->
<!-- color: green (default) · blue · pink · gold -->
<body data-rs-widgets="pixel-pet" data-rs-pixel-pet-name="Blobby" data-rs-pixel-pet-color="green">

↑ top


smilies: Smilies

The forum smiley pack, type the code, see the face, running since the phpBB age.

Turns :) and friends into pixel emoticons.

informative

Options: data-rs-smilies-<name>
OptionTypeDefaultNotes
scope selector - the subtree to parse; defaults to the host
src string - override the smilies pack image URL (same-origin)

Accessibility: Each image's alt is the original typed code, so copy-paste and screen readers round-trip. The parser skips code, kbd, inputs, and any [data-rs-no-smilies] region.

example
<!-- codes in this element become pixel faces: :) ;) :D -->
<div data-rs-widgets="smilies">Welcome!! :) Sign my guestbook ;)</div>

↑ top


snowfall: Snowfall

The JavaScript snow script every winter homepage installed in December.

Falling snow (or hearts, or confetti) on a canvas.

decorative

Options: data-rs-snowfall-<name>
OptionTypeDefaultNotes
density light · normal · blizzard normal or a number, capped at 100
glyph star · heart · leaf · confetti - default is a pixel flake
speed slow · normal · fast normal
wind number 0 -2 to 2
melt bool true

Accessibility: Decoration: canvas is aria-hidden, pointer-events:none. Never mounts under prefers-reduced-motion; the shared loop pauses when the tab is hidden.

example
<body data-rs-widgets="snowfall" data-rs-snowfall-density="light">

↑ top


sparkle: Sparkle

The glitter-graphics and sparkle-trail scene of the kawaii and shrine pages.

Twinkles at the pointer, or ambient glitter on a zone.

decorative

Options: data-rs-sparkle-<name>
OptionTypeDefaultNotes
mode pointer · ambient · both pointer
trigger move · click move
palette gold · ice · rainbow · theme theme
color color - one legal color; overrides palette
zone selector - ambient area; defaults to the host

Accessibility: Decoration: aria-hidden, pointer-events:none, silent under prefers-reduced-motion.

example
<body data-rs-widgets="sparkle" data-rs-sparkle-mode="both" data-rs-sparkle-palette="gold">

↑ top


starfield: Starfield

The flying-through-space screensaver, reborn as a page background.

Warp-speed stars on a canvas.

decorative

Options: data-rs-starfield-<name>
OptionTypeDefaultNotes
density sparse · normal · dense normal or a number, capped at 200
speed slow · normal · fast normal
layers int 2 1 to 3 parallax layers

Accessibility: Decoration: aria-hidden, pointer-events:none. Under prefers-reduced-motion it becomes a still sprinkle of stars, the look without the flight.

example
<body data-rs-widgets="starfield">

↑ top


ticker: Ticker

The window.status scrollers that welcomed you to a thousand homepages.

A scrolling status-bar text ticker.

informative

Options: data-rs-ticker-<name>
OptionTypeDefaultNotes
messages json-array - the lines to scroll
source selector - read <li>s from this element instead
speed slow · normal · fast normal
dock bottom · none none
separator string +++

Accessibility: Informative: the messages are ordinary readable text (never aria-live). Pauses on hover and focus. Under prefers-reduced-motion it swaps the message every 5s instead of scrolling.

example
<p class="rs-statusbar" data-rs-widgets="ticker"
   data-rs-ticker-messages='["Welcome to my page!!","Sign my guestbook"]'></p>

↑ top


transitions: Page transitions

The PowerPoint-meets-Internet-Explorer scene change, tamed.

Page-to-page wipes via the View Transitions API.

decorative

Options: data-rs-transitions-<name>
OptionTypeDefaultNotes
style wipe · box-in · box-out · dissolve · checker wipe

Accessibility: Pure progressive enhancement: where the API is unsupported, navigation is instant and unbroken. The API honors reduced motion on its own; the widget also skips under it.

example
<body data-rs-widgets="transitions">

↑ top


webring: Webring

Sites holding hands in a circle, curated by a ringmaster.

Fills an rs-webring-bar from a ring's JSON, computing prev/random/next.

informative

Options: data-rs-webring-<name>
OptionTypeDefaultNotes
src url - the ring JSON; or an inline <script type=application/json>

Accessibility: Plain links, fully functional as static HTML. Works from an inline ring with no service at all.

example
<div class="rs-webring-bar" data-rs-widgets="webring" data-rs-webring-src="ring.json"></div>

↑ top


windows: Window manager

The draggable fake-window scripts of the DHTML era.

Makes rs-window elements draggable, with click-to-front (or dispatch rs:window:raise) and a five-window cap.

informative

Events: rs:window:focus

Accessibility: Drag by the titlebar with mouse, touch, or the keyboard (focus the titlebar, arrow to move, Shift for fine steps, Home to reset). Dragging is disabled below the svga breakpoint, where windows render in flow. Five windows maximum.

example
<body data-rs-widgets="windows">
  <div class="rs-window" data-rs-window="floating">…titlebar + body…</div>

↑ top