# Parvus is a dependency-free lightbox that treats accessibility as the goal

> Parvus is a JavaScript image lightbox with no runtime dependencies, built so that a gallery can be marked up in ordinary HTML and enhanced with captions, copyright notices, grouping, responsive sources and localisation through data attributes and a small options object. It is MIT licensed, ships as CSS and JavaScript files you can link directly, and is also on npm.

**deoostfrees/Parvus** — An open source, dependency free image lightbox with the goal of being accessible.

- Repository: https://github.com/deoostfrees/Parvus
- Website: https://codepen.io/collection/DwLBpz
- Stars: 538 · Forks: 15
- Language: JavaScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/deoostfrees-parvus

## The premise: an overlay that needs nothing else

Parvus opens with a blunt sentence: Overlays suck, but if you need one, consider using Parvus.

Then the description. Parvus is an open source, dependency free image lightbox with the goal of being accessible.

Two claims there, and the second one is the interesting one. A dependency-free lightbox is not remarkable on its own; there are plenty of small ones. What is unusual is that accessibility is the stated goal rather than a feature that came along later, and the topics list backs that up with a11y and accessibility alongside dependency-free, javascript, lightbox and vanilla.

The dependency-free part is literal. The package.json has no dependencies field at all, only devDependencies, so nothing in the shipped bundle comes from elsewhere. The whole list of devDependencies is build tooling: Babel, Rollup and its plugins, core-js, PostCSS, Sass, the standard linter and Stylelint with its SCSS config. The browser support section of the README lists the globals Parvus relies on, and those are Image, history, CustomEvent, requestAnimationFrame and getComputedStyle, all of which are platform APIs rather than libraries.

The primary language is JavaScript, the licence is MIT, and the homepage is a CodePen collection, which is a deliberate choice: you can see the library running and pick it apart before you install anything. The last push was on 2026-09-09, alongside a 3.2.0-rc release, and the repository is not archived.

## Installing it, either as files or as a package

There are two routes, and the first one needs no build step at all.

The download route gives you a CSS file and a JavaScript file, in minified and un-minified versions: dist/css/parvus.min.css or dist/css/parvus.css, and dist/js/parvus.min.js or dist/js/parvus.js. You link both in your HTML:

```html
<link href="path/to/parvus.min.css" rel="stylesheet">
```

in the head, and:

```html
<script src="path/to/parvus.min.js"></script>
```

at the end of the body.

That is the whole installation for the direct-download route, and it is the route that makes the dependency-free claim meaningful: no package manager, no bundler configuration, no transpiler.

The package manager route is npm or yarn:

```sh
npm install parvus
```

or:

```sh
yarn add parvus
```

After installation you import Parvus into your JavaScript codebase:

```js
import Parvus from 'parvus'
```

and the README reminds you to include the corresponding SCSS or CSS file.

That last sentence is the one people miss. The package ships JavaScript and CSS, and importing the module does not bring the styles with it, so a build that imports Parvus and forgets the stylesheet produces a lightbox with no layout. The package.json records the style entry at dist/css/parvus.css and the module entry at dist/js/parvus.esm.js, with the main field pointing at dist/js/parvus.js, so the exports map covers both the CommonJS and the ES module build.

## Usage is markup, and the enhancement is optional

The trigger is an ordinary link with a class.

```html
<a href="path/to/image.jpg" class="lightbox">
  <img src="path/to/thumbnail.jpg" alt="">
```

And the script initialises with one line:

```js
const prvs = new Parvus()
```

That is the whole integration for a simple gallery, and the design consequence is significant. The markup you write is a link to the full-size image with a thumbnail inside it, which means the page works before JavaScript runs, and it still works if JavaScript never runs. The lightbox is an enhancement of a link, not a replacement for it.

That is also why the alt attribute is empty in the examples. In a link-to-full-size-image pattern, the thumbnail is decorative and the link text is the image, so an empty alt is the accessible choice rather than an oversight. A library that took over rendering would make that decision for you, and could get it wrong.

The README's opening line about overlays is the complaint the design answers. A lightbox that renders into a container the author does not control takes over the page, and on a site that has its own navigation, its own modals and its own focus handling, that collision is where accessibility tends to break. Here the overlay is added on top, and the underlying link is still there.

Everything else follows the same pattern. Captions, copyright, galleries, responsive sources and localisation are all expressed as attributes on the trigger element or as options on the constructor, and the library reads them rather than asking you to build a configuration object per image.

## Captions, copyright and galleries, three ways each

Each of the three content features has three ways of being supplied, and the pattern is consistent across all of them.

Captions can be referenced by ID, which is the first method. You put an ID on your caption element and reference it from the trigger with the data-caption-id attribute, so the caption lives outside the link, which is what lets it be a real figcaption in a figure element.

Or you can put the caption directly on the trigger with a data-caption attribute.

Or you can set the captionsSelector option to select a caption from a child element's innerHTML, which is the third method and the one that scales across a gallery:

```js
const prvs = new Parvus({
  captionsSelector: '.figure__caption',
})
```

Copyright works the same way, with data-copyright-id, data-copyright and a copyrightSelector option, and the element is a small element marked hidden in the markup, which means the copyright text is in the document but not shown until the image is opened. That is a real accessibility property rather than a cosmetic one, because the information is present in the accessibility tree in the closed state too.

Galleries have two routes as well. The first is a data-group attribute on each trigger, where the value names the group, so several triggers sharing the value form one set.

The second is the gallerySelector option, which groups all images with a specific class within a selector, so you can mark up a whole container:

```js
const prvs = new Parvus({
  gallerySelector: '.gallery',
})
```

The option overrides the data-group attribute, which the README states, so the selector approach is the one that wins if both are present. That is a sensible precedence: an explicit container is a stronger statement about which images belong together than a per-image attribute.

## Options, responsive images and localisation

The options object is where the behaviour lives, and the README lists it with comments. The selector option chooses the elements that trigger Parvus, defaulting to .lightbox. There are flags for the zoom indicator, for displaying captions, and for displaying copyright. There are separate options for where the caption or copyright is displayed and which attribute to read it from, and the README notes that the selector uses self for the a tag itself.

The interaction options are the interesting ones. docClose controls whether clicking outside closes Parvus. swipeClose controls whether you can close it by swiping up or down. simulateTouch accepts mouse events like touch events, which means click and drag to change slides works with a mouse on a device where the pointer is treated as a mouse rather than a touch pointer.

The defaults are conservative: docClose and swipeClose are both true, and zoomIndicator and the caption and copyright flags are on. The example in the README shows the alternative:

```js
const prvs = new Parvus({
  // Clicking outside does not close Parvus
  docClose: false
})
```

Responsive images are handled with the data-srcset and data-sizes attributes on the trigger, using the same syntax as a native srcset, so the browser picks the right source for the viewport before the lightbox ever loads.

Localisation is a separate import. You import the language module and set it as an option:

```js
import de from 'parvus/src/l10n/de'

const prvs = new Parvus({
  l10n: de
})
```

That import path is worth noticing, because it reaches into the source tree rather than the package root, which tells you the translations are shipped as separate modules and that you pick the one you want rather than getting them all. The default is a neutral language, and any interface text the lightbox itself generates, the buttons and the counter, comes from this module.

## Plugins, events and where Parvus stops

The README's table of contents has three sections for the extension points, and they are the part that decides whether Parvus fits a project.

There is an API section, an Events section and a Plugins section, with subsections for using plugins, creating plugins and plugin hooks. The presence of a plugin system in a library with no dependencies is notable, because it means the extension mechanism is part of the library rather than something bolted on, and it means a plugin is written against Parvus's own hooks rather than against a general framework.

The events section matters for the accessibility goal. A lightbox has to tell the rest of the page what it is doing, and it has to manage focus while it is open, and an event stream is how a page that has its own keyboard handling learns that a lightbox has opened or closed. That is the mechanism by which a Parvus overlay can coexist with a site that also manages focus, rather than fighting it.

The limits are worth stating plainly. Parvus is a lightbox, not a gallery framework: there is no theming system beyond the stylesheet you link, no animation configuration, no carousel beyond what grouping gives you, and no attempt to be the only modal on the page. The browser support section is where the compatibility story lives, and the browserslist in package.json is deliberately conservative, last 2 versions and > 1% and not dead.

There is also the version situation to note. The package.json version is 3.1.1, but the releases listed are 3.2.0-rc from 2026-09-09 and 3.2.0-beta from 2026-09-04, after 3.1.1 from 2026-06-27. So the released line is 3.1.1 and 3.2 is still working through its pre-releases. A project that pins to a stable version gets 3.1.1 and not the 3.2 features.

The build itself is Rollup with PostCSS and Sass, and the test and lint scripts are wired into the build so that a style regression or a lint failure stops a release. The linting uses standard for JavaScript and Stylelint with the SCSS config for the styles.

For adoption, the honest summary is that Parvus is for the case where you want a lightbox that leaves your markup alone and your bundle alone. If you need something you can reshape into a different product, the plugin hooks are where you would start.

## Conclusion

Parvus suits a project that needs a lightbox without adding a framework or a runtime dependency to its bundle, since the markup stays ordinary links and images and the enhancement is progressive, so the page still works without JavaScript. It is the wrong tool if you want a highly customisable overlay with themes, transitions and a large plugin ecosystem, because the whole point here is that Parvus has no dependencies and a deliberately small option set, and it is the wrong tool if you need to work in a framework that owns the DOM, since Parvus attaches behaviour to existing elements rather than rendering them. Before you adopt it, check the browser support page and the events and plugin sections, because the plugin hooks are the part that decides whether you can extend it the way your project needs.

## FAQ

### What is Parvus?

It is an open source, dependency free image lightbox with the goal of being accessible. The JavaScript ships with no runtime dependencies at all, and it enhances ordinary links and images rather than replacing them, so a page still works without it.

### How do I install Parvus?

Either download the CSS and JavaScript from dist and link them in your HTML, or install the package with npm install parvus or yarn add parvus and import it with import Parvus from 'parvus'. Either way you must also include the corresponding SCSS or CSS file, since importing the module does not bring the styles with it.

### How do I add a caption to a Parvus image?

Three ways: reference a caption element by ID with the data-caption-id attribute, put the text directly on the trigger with data-caption, or set the captionsSelector option to read the caption from a child element's innerHTML, for example captionsSelector: '.figure__caption'.

### How do I group images into a gallery in Parvus?

Add the same data-group value to each trigger you want grouped, or set the gallerySelector option to group all images with a specific class within a selector. The gallerySelector option overrides the data-group attribute.

### How does Parvus handle responsive images?

With the data-srcset and data-sizes attributes on the trigger element, using the same syntax as a native srcset, so the browser selects the right source for the viewport before the lightbox loads. There is also a zoomIndicator option to display a zoom indicator.

### Can I extend Parvus with plugins?

Yes. The README documents using plugins, creating plugins and the plugin hooks, so extensions are written against Parvus's own hook system rather than a general framework. The library also emits events, which is how a page with its own keyboard and focus handling can react to the lightbox opening and closing.

## Sources

- [deoostfrees/Parvus on GitHub](https://github.com/deoostfrees/Parvus)
- [License: MIT](https://github.com/deoostfrees/Parvus/blob/main/LICENSE)
- [Project website](https://codepen.io/collection/DwLBpz)
- [README](https://github.com/deoostfrees/Parvus/blob/main/README.md)
- [Releases](https://github.com/deoostfrees/Parvus/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/deoostfrees-parvus
