widthwise
v0.1.1
Published
Declarative responsive layout testing — Galen-style YAML rules driven through a headless browser.
Downloads
275
Maintainers
Readme
widthwise
Declarative responsive layout testing — Galen-style rules in plain YAML, driven through a headless browser. No Java, no custom DSL, no pixel diffs: assert layout facts ("the sidebar is hidden on mobile", "the nav sits above the content") per viewport, and gate CI on the result.
Install
npm i -D widthwise playwright # or puppeteer — bring your own driver
npx playwright install chromiumThe browser driver is an optional peer dependency; widthwise auto-detects whichever one is installed (Playwright is preferred, --adapter overrides).
Spec files
# specs/home.yml
url: /
viewports:
mobile: 375 # width only; height defaults to 800
tablet: 768
desktop: { width: 1280, height: 900 }
rules:
- selector: .sidebar
at: [mobile] # named viewports, so specs survive breakpoint changes
visible: false
- selector: .hero
at: [mobile, tablet]
width: 100vw # px, %, vw, vh — numbers mean px
- selector: .nav
above: .main-content # omit `at` to assert at every viewportRule assertions:
| Key | Value | Meaning |
| --- | --- | --- |
| visible | true / false | true: at least one match is visible. false: none are (absent passes). |
| width, height | 320, "320px", "50%", "100vw", "40vh" | Every matched element must be within tolerance (default ±1px). % is relative to the parent element. |
| above, below, left-of, right-of | CSS selector | Edge comparison between the first match of each selector. |
| inside | CSS selector | Full containment within the target's box. |
| tolerance | number (px) | Slack applied to the rule's dimension/relation checks. |
Config
Optional widthwise.config.json in the project root:
{
"baseUrl": "http://localhost:3000",
"specs": ["specs/**/*.yml"],
"viewports": { "mobile": 375, "tablet": 768, "desktop": 1280 },
"reporters": ["console", "html"],
"outDir": "widthwise-report"
}Config viewports are defaults; a spec's own viewports block overrides them by name.
CLI
npx widthwise run # uses config + default globs
npx widthwise run ./specs/*.yml -b http://localhost:3000 -r console,htmlExit codes: 0 all checks passed, 1 check failures, 2 config/spec errors — so the run gates a PR directly.
# CI usage
- run: npx widthwise run -r console,html
- uses: actions/upload-artifact@v4
if: always()
with:
name: widthwise-report
path: widthwise-report/Programmatic API
import { runTests } from 'widthwise'
const result = await runTests({
baseUrl: 'http://localhost:3000',
specs: ['specs/**/*.yml'],
})
if (!result.passed) process.exit(1)parseSpec, runSpec, evaluateRule, and the BrowserAdapter interface are exported too, so you can plug in a custom driver or embed the evaluator in another harness.
