Skip to content

Add optional jspsych-mobile.css stylesheet - #3711

Open
jodeleeuw wants to merge 1 commit into
mainfrom
feature/mobile-stylesheet
Open

Add optional jspsych-mobile.css stylesheet#3711
jodeleeuw wants to merge 1 commit into
mainfrom
feature/mobile-stylesheet

Conversation

@jodeleeuw

Copy link
Copy Markdown
Member

Summary

Adds css/jspsych-mobile.css, an opt-in stylesheet that moves response buttons into a bar fixed to the bottom of the screen on touch devices, where they are within reach of a thumb rather than requiring a stretch to the middle of the page.

<link rel="stylesheet" href=".../jspsych/css/jspsych.css">
<link rel="stylesheet" href=".../jspsych/css/jspsych-mobile.css">

jspsych.css is untouched. Every rule in the new file sits inside @media (pointer: coarse), so loading it is inert on mouse-driven devices.

Why opt-in rather than a default

A fixed bottom bar is wrong for tasks whose response positions carry spatial meaning — a Simon task, or any design where button location is part of the manipulation. That makes it a choice the experimenter should make, not a default.

Two implementation details worth a look

It targets .jspsych-btn-group-flex and .jspsych-btn-group-grid, not #jspsych-html-button-response-btngroup. Those layout classes are defined in jspsych.css and applied by all five *-button-response plugins, so one rule covers audio, canvas, html, image, and video. Targeting a single plugin's element id would both miss the other four and put plugin-specific knowledge into core CSS.

It deliberately does not set display. The default button_layout is "grid", and the plugin sets gridTemplateColumns and gridTemplateRows as inline styles. A display: flex here would discard that configuration, and a stylesheet rule cannot override the inline grid template anyway. Centering continues to come from jspsych.cssjustify-content for flex, auto margins for grid.

Build change

webpack.config.cjs now takes a named entry per stylesheet and emits [name].css. The .woff cleanup is still scoped to jspsych.css, since only index.scss imports the fonts.

I verified that css/jspsych.css is byte-identical before and after this change (ac0058cd081280f52ae6a7284b65240d) by rebuilding with the previous single-entry config and comparing, so the multi-entry switch is purely additive.

Changes

  • packages/jspsych/src/mobile.scss — new
  • packages/jspsych/webpack.config.cjs — named entries, [name].css
  • docs/overview/style.md — usage, the caveat about spatial tasks, and the two rules most likely to need overriding
  • .changeset/mobile-stylesheet.mdminor

Notes for review

  • The changeset is minor since this is purely additive, but it is milestoned to 9.0 to ship alongside Increase default button and input sizing #3710.
  • The bar is painted with background: Canvas so it is correct in both light and dark mode. Experiments that set their own page background need to override it; the docs say so.
  • padding-bottom: 112px on .jspsych-content assumes default button metrics. It is documented as something to raise if buttons are enlarged, but a more robust approach is welcome if you'd prefer one.

🤖 Generated with Claude Code

Adds css/jspsych-mobile.css, an opt-in stylesheet that pins response
buttons to a bar at the bottom of the screen on touch devices, within
reach of a thumb.

The rules target .jspsych-btn-group-flex and .jspsych-btn-group-grid
rather than a single plugin's element id, so they cover the audio,
canvas, html, image, and video button-response plugins alike. `display`
is left alone because the grid layout sets grid-template-columns and
-rows inline, which a stylesheet rule could not override.

Kept out of jspsych.css because a fixed bar is wrong for tasks whose
response positions carry spatial meaning. Every rule sits inside a
`pointer: coarse` query, so loading it is inert on mouse-driven devices.

The webpack config now takes a named entry per stylesheet and emits
`[name].css`. Verified that css/jspsych.css is byte-identical to the
output of the previous single-entry config.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jodeleeuw jodeleeuw added this to the 9.0 milestone Jul 28, 2026
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 081480e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
jspsych Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

📦 Preview build ready

Built from PR head 081480e and published at 768a5fa on branch preview/pr-3711.
URLs below are pinned to an immutable commit SHA, so they are safe to share and are cached permanently by jsDelivr.

Changed packages: jspsych

Quick-start HTML:

<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/jspsych/dist/index.browser.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/jspsych/css/jspsych.css">
<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-html-keyboard-response/dist/index.browser.min.js"></script>
All package URLs
  • @jspsych/extension-mouse-trackinghttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/extension-mouse-tracking/dist/index.browser.min.js
  • @jspsych/extension-record-videohttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/extension-record-video/dist/index.browser.min.js
  • @jspsych/extension-webgazerhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/extension-webgazer/dist/index.browser.min.js
  • jspsychhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/jspsych/dist/index.browser.min.js
  • @jspsych/plugin-animationhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-animation/dist/index.browser.min.js
  • @jspsych/plugin-audio-button-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-audio-button-response/dist/index.browser.min.js
  • @jspsych/plugin-audio-keyboard-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-audio-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-audio-slider-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-audio-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-browser-checkhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-browser-check/dist/index.browser.min.js
  • @jspsych/plugin-call-functionhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-call-function/dist/index.browser.min.js
  • @jspsych/plugin-canvas-button-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-canvas-button-response/dist/index.browser.min.js
  • @jspsych/plugin-canvas-keyboard-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-canvas-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-canvas-slider-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-canvas-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-categorize-animationhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-categorize-animation/dist/index.browser.min.js
  • @jspsych/plugin-categorize-htmlhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-categorize-html/dist/index.browser.min.js
  • @jspsych/plugin-categorize-imagehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-categorize-image/dist/index.browser.min.js
  • @jspsych/plugin-clozehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-cloze/dist/index.browser.min.js
  • @jspsych/plugin-external-htmlhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-external-html/dist/index.browser.min.js
  • @jspsych/plugin-free-sorthttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-free-sort/dist/index.browser.min.js
  • @jspsych/plugin-fullscreenhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-fullscreen/dist/index.browser.min.js
  • @jspsych/plugin-html-audio-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-html-audio-response/dist/index.browser.min.js
  • @jspsych/plugin-html-button-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-html-button-response/dist/index.browser.min.js
  • @jspsych/plugin-html-keyboard-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-html-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-html-slider-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-html-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-html-video-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-html-video-response/dist/index.browser.min.js
  • @jspsych/plugin-iat-htmlhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-iat-html/dist/index.browser.min.js
  • @jspsych/plugin-iat-imagehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-iat-image/dist/index.browser.min.js
  • @jspsych/plugin-image-button-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-image-button-response/dist/index.browser.min.js
  • @jspsych/plugin-image-keyboard-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-image-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-image-slider-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-image-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-initialize-camerahttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-initialize-camera/dist/index.browser.min.js
  • @jspsych/plugin-initialize-microphonehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-initialize-microphone/dist/index.browser.min.js
  • @jspsych/plugin-instructionshttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-instructions/dist/index.browser.min.js
  • @jspsych/plugin-maxdiffhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-maxdiff/dist/index.browser.min.js
  • @jspsych/plugin-mirror-camerahttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-mirror-camera/dist/index.browser.min.js
  • @jspsych/plugin-preloadhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-preload/dist/index.browser.min.js
  • @jspsych/plugin-reconstructionhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-reconstruction/dist/index.browser.min.js
  • @jspsych/plugin-resizehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-resize/dist/index.browser.min.js
  • @jspsych/plugin-same-different-htmlhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-same-different-html/dist/index.browser.min.js
  • @jspsych/plugin-same-different-imagehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-same-different-image/dist/index.browser.min.js
  • @jspsych/plugin-serial-reaction-time-mousehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-serial-reaction-time-mouse/dist/index.browser.min.js
  • @jspsych/plugin-serial-reaction-timehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-serial-reaction-time/dist/index.browser.min.js
  • @jspsych/plugin-sketchpadhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-sketchpad/dist/index.browser.min.js
  • @jspsych/plugin-survey-html-formhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-survey-html-form/dist/index.browser.min.js
  • @jspsych/plugin-survey-likerthttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-survey-likert/dist/index.browser.min.js
  • @jspsych/plugin-survey-multi-choicehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-survey-multi-choice/dist/index.browser.min.js
  • @jspsych/plugin-survey-multi-selecthttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-survey-multi-select/dist/index.browser.min.js
  • @jspsych/plugin-survey-texthttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-survey-text/dist/index.browser.min.js
  • @jspsych/plugin-surveyhttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-survey/dist/index.browser.min.js
  • @jspsych/plugin-video-button-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-video-button-response/dist/index.browser.min.js
  • @jspsych/plugin-video-keyboard-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-video-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-video-slider-responsehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-video-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-virtual-chinresthttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-virtual-chinrest/dist/index.browser.min.js
  • @jspsych/plugin-visual-search-circlehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-visual-search-circle/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-calibratehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-webgazer-calibrate/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-init-camerahttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-webgazer-init-camera/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-validatehttps://cdn.jsdelivr.net/gh/jspsych/jsPsych@768a5fa622f4a30fbd9121caffc665bf02e5e035/packages/plugin-webgazer-validate/dist/index.browser.min.js

Last updated 2026-07-28 23:13 UTC for PR head 081480e.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant