JavaScript

Jellyfish is a CSS framework first. The bundled script (dist/js/jellyfish.min.js) powers the navbar, modals, theme toggle and font-size control, drives the accordion and responsive-table enhancements, and adds these general helpers. No inline handlers — everything is delegated, so it works under a CSP.

Cookies


    jfSetCookie(name, value, days);
    jfGetCookie(name);       // → string
    jfDestroyCookie(name);
  

Debouncing

jfDebounce(eventType, fn, timeout?, args?) attaches a debounced window listener — use it for scroll / resize work. timeout defaults to 300ms; if you omit it, args can take its place. fn is called with args spread as its arguments, not with the native event — read window state directly inside it if you need it.


    // Default 300ms timeout
    function onResize() {
      console.log(window.innerWidth);
    }
    jfDebounce("resize", onResize);

    // Custom timeout
    function onScroll() {
      console.log(window.scrollY);
    }
    jfDebounce("scroll", onScroll, 150);

    // Skip the timeout (keeps the 300ms default), pass args instead — spread
    // into the handler as its arguments, e.g. fn("nav") below
    function logSection(label) {
      console.log(label, window.scrollY);
    }
    jfDebounce("scroll", logSection, ["nav"]);
  

Theme

theme.js owns the .jf-dark / .jf-light class on <html>, stored in localStorage["jf-theme"] as "dark" / "light" / "system" (absent = hasn't chosen). A stored choice outranks the data-default-theme the site declares, which in turn outranks the OS. Wire-up and the pre-paint snippet are on Theme Colours.


    JellyfishTheme.get();          // "dark" | "light"  (resolved)
    JellyfishTheme.set("dark");    // "dark" | "light" | "system"
    JellyfishTheme.toggle();
  

Markup: [data-theme-toggle] toggles, [data-theme-set="dark|light|system"] sets a mode. A jfThemeChange event fires on <html> with detail.theme.

Accordions & modals

Both are native and need no script to work. The bundled JS only adds events and conveniences — see Accordions and Modals. All four events (jfAccordionOpened / Closed, jfModalOpened / Closed) also push to window.dataLayer when GTM is present.

Lazy-loading background images

For a CSS background-image (which has no native loading="lazy"), add .has-bg-img and a data-bg-img pointing at the full-size image. Set a small placeholder inline; jfLazyLoadBackgroundImage swaps in the real one via IntersectionObserver as it nears the viewport. For a normal <img>, use the native loading="lazy" instead.

Give it a size

.has-bg-img carries no CSS of its own — it's purely a JS hook. A <div> with only a background-image and no content has no intrinsic size, so without an explicit height (or aspect-ratio, or a .ratio-* class) it renders at zero height and the image is invisible either way, placeholder or not.