Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions baselines/size-baseline.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"capturedAt": "2026-07-20T02:29:06.425Z",
"capturedAt": "2026-08-09T15:15:46.109Z",
"branch": "v1",
"buildCommand": "yarn build",
"browserslist": [
Expand All @@ -10,10 +10,10 @@
"ios >= 15.4"
],
"source": "src/chota.css",
"dist_chota_css_bytes": 17768,
"dist_chota_min_css_bytes": 14080,
"gzip_bytes": 3326,
"dist_chota_css_bytes": 18733,
"dist_chota_min_css_bytes": 14353,
"gzip_bytes": 3372,
"gzip_ceiling_bytes": 4096,
"gzip_budget_remaining_bytes": 770,
"rationale": "14-byte increase from scoped media query wrapping: `@media (max-width: 480px) { .grouped { flex-wrap: wrap; } }`. Wrapping is only enabled on narrow viewports (<480px), preserving existing horizontal grouped-control behavior on desktop. This avoids the width:100% input issue that would occur with unscoped flex-wrap. All target browsers support @media queries."
"gzip_budget_remaining_bytes": 724,
"rationale": "card #139 focus-visible restoration: replaced outline:none + box-shadow :focus rule with a visible :focus-visible ring (outline: 2px solid var(--color-primary); outline-offset: 2px) on all interactive elements (a, button, input, select, textarea, summary, [tabindex]) plus a form-control :focus-visible rule preserving border-color + box-shadow. 46-byte gzip increase from the added grouped outline rule. :focus-visible is supported across the entire browserslist (Safari 15.4+, iOS 15.4+, latest Chrome/Firefox/Edge), so no :focus fallback is needed."
}
25 changes: 20 additions & 5 deletions dist/chota.css
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,20 @@ button {
button:hover {
opacity: 0.8;
}
/* Visible keyboard focus for all interactive elements.
:focus-visible is supported across the entire browserslist
(Safari 15.4+, iOS 15.4+, latest Chrome/Firefox/Edge),
so no :focus fallback is needed. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
input:disabled,
input:disabled:hover,
button:disabled,
Expand Down Expand Up @@ -477,11 +491,12 @@ textarea:hover,
textarea[type="text"]:hover {
border-color: var(--color-grey);
}
input:not([type="checkbox"], [type="radio"], [type="submit"], [type="color"], [type="button"], [type="reset"]):focus,
select:focus,
textarea:focus,
textarea[type="text"]:focus {
outline: none;
/* Form controls retain their existing border + box-shadow focus
signal, but only on keyboard focus (not mouse click). */
input:not([type="checkbox"], [type="radio"], [type="submit"], [type="color"], [type="button"], [type="reset"]):focus-visible,
select:focus-visible,
textarea:focus-visible,
textarea[type="text"]:focus-visible {
border-color: var(--color-primary);
box-shadow: 0 0 1px var(--color-primary);
}
Expand Down
2 changes: 1 addition & 1 deletion dist/chota.min.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"postcss": "postcss ./src/chota.css -d dist/",
"watch": "cross-env NODE_ENV=development postcss ./src/chota.css -d dist/ -w",
"test": "stylelint src/*.css",
"test:vrt": "npx playwright test test/vrt/index.spec.js test/vrt/components.spec.js test/vrt/grouped-controls.spec.js test/vrt/validations/mobile-grid-overflow.spec.js test/vrt/validations/nav-logo-full-height.spec.js test/vrt/validations/spacing-utilities.spec.js test/vrt/dark-mode.spec.js test/vrt/breakpoints.spec.js --project=chromium",
"test:a11y": "npx playwright test test/vrt/a11y.spec.js test/vrt/a11y-keyboard.spec.js --project=chromium",
"test:vrt": "npx playwright test test/vrt/index.spec.js test/vrt/components.spec.js test/vrt/grouped-controls.spec.js test/vrt/validations/mobile-grid-overflow.spec.js test/vrt/validations/nav-logo-full-height.spec.js test/vrt/validations/spacing-utilities.spec.js test/vrt/dark-mode.spec.js test/vrt/breakpoints.spec.js test/vrt/keyboard-focus.spec.js --project=chromium",
"test:a11y": "npx playwright test test/vrt/a11y.spec.js --project=chromium",
"test:api": "npx playwright test test/vrt/api.spec.js --project=chromium",
"baselines": "node scripts/baselines.js"
},
Expand Down
26 changes: 21 additions & 5 deletions src/_form.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ button:hover {
opacity: 0.8;
}

/* Visible keyboard focus for all interactive elements.
:focus-visible is supported across the entire browserslist
(Safari 15.4+, iOS 15.4+, latest Chrome/Firefox/Edge),
so no :focus fallback is needed. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}

input:disabled,
input:disabled:hover,
button:disabled,
Expand Down Expand Up @@ -127,11 +142,12 @@ textarea[type="text"]:hover {
border-color: var(--color-grey);
}

input:not([type="checkbox"], [type="radio"], [type="submit"], [type="color"], [type="button"], [type="reset"]):focus,
select:focus,
textarea:focus,
textarea[type="text"]:focus {
outline: none;
/* Form controls retain their existing border + box-shadow focus
signal, but only on keyboard focus (not mouse click). */
input:not([type="checkbox"], [type="radio"], [type="submit"], [type="color"], [type="button"], [type="reset"]):focus-visible,
select:focus-visible,
textarea:focus-visible,
textarea[type="text"]:focus-visible {
border-color: var(--color-primary);
box-shadow: 0 0 1px var(--color-primary);
}
Expand Down
10 changes: 5 additions & 5 deletions test/components.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ <h1>Nav</h1>
<br />
<nav class="nav">
<div class="nav-left">
<a class="brand" href="#">
<a class="brand" href="#" aria-label="Brand">
<img
src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"
alt=""
Expand Down Expand Up @@ -436,11 +436,11 @@ <h1>Icons</h1>
alt="icon"
/>
</button>
<button class="button icon-only">
<img src="https://icongr.am/feather/search.svg?size=16" />
<button class="button icon-only" aria-label="Search">
<img src="https://icongr.am/feather/search.svg?size=16" alt="Search" />
</button>
<button class="button icon-only">
<img src="https://icongr.am/feather/search.svg" />
<button class="button icon-only" aria-label="Search">
<img src="https://icongr.am/feather/search.svg" alt="Search" />
</button>
</section>
</div>
Expand Down
4 changes: 2 additions & 2 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,8 @@ <h1>Form elements</h1>
id="input__text6"
placeholder="Search"
/>
<button class="button icon-only">
<img src="https://icongr.am/feather/search.svg?size=16" />
<button class="button icon-only" aria-label="Search">
<img src="https://icongr.am/feather/search.svg?size=16" alt="Search" />
</button>
</p>
<p class="grouped">
Expand Down
18 changes: 17 additions & 1 deletion test/vrt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ test/vrt/
├── grouped-controls.spec.js # Grouped controls — computed-style assertions, no screenshots (Chromium)
├── browser-smoke.spec.js # Firefox/WebKit smoke tests (no screenshots)
├── a11y.spec.js # Accessibility baseline (Chromium)
├── a11y-keyboard.spec.js # Keyboard focus observations (Chromium)
├── keyboard-focus.spec.js # Keyboard focus — :focus-visible assertions + focus-state VRT baselines (Chromium)
├── api.spec.js # CSS public API contract (Chromium)
├── dark-mode.spec.js # Dark-mode custom properties (Chromium)
├── breakpoints.spec.js # Responsive breakpoint boundaries (Chromium)
├── vrt-helpers.js # Fixture server startup logic
├── snapshots/ # Reference screenshots (git-tracked, Playwright-managed)
│ ├── index.spec.js-snapshots/ # toMatchSnapshot baselines (index.html)
│ ├── components.spec.js-snapshots/ # toMatchSnapshot baselines (components.html)
│ ├── keyboard-focus.spec.js-snapshots/ # toMatchSnapshot baselines (keyboard-focus, focus-state)
│ └── validations/ # Validation test specs
│ └── nav-logo-full-height.spec.js-snapshots/ # toMatchSnapshot baselines (nav-logo-full-height.html)
├── report/ # HTML report (git-ignored, generated on each run)
Expand All @@ -55,6 +56,7 @@ test/vrt/
| VRT — per-section element (locator screenshot) | Chromium | index.html, components.html | Desktop (1280×720) only |
| Computed-style (no screenshots) | Chromium | index.html, components.html, mobile-grid-overflow.html, grouped-controls.html, dark-mode, breakpoints | Desktop (1280×720), Mobile (375×667), narrow (320×568/414×896) |
| A11y (axe-core) | Chromium | index.html | Desktop (1280×720) |
| Keyboard focus (computed-style + VRT) | Chromium | index.html, components.html | Desktop (1280×720) |
| API (selectors) | Chromium | dist/chota.css | N/A |
| Smoke (functional) | Firefox, WebKit | index.html, components.html | Desktop (1280×720), Mobile (375×667) |

Expand All @@ -67,6 +69,20 @@ Per-section element screenshots target a stable section root:

The full-page and per-section captures supplement (do not replace) the first-fold baselines `index-desktop.png`, `index-mobile.png`, `components-desktop.png`, and `components-mobile.png`, which are preserved unchanged.

### Keyboard focus (#139)

`keyboard-focus.spec.js` replaces the former observation-only `a11y-keyboard.spec.js` (which logged focus data but never failed). The new spec uses real keyboard interactions (`page.keyboard.press('Tab')`) and asserts the resulting focus target plus its computed outline:

- Real `Tab` navigation until the target selector is `document.activeElement` (not `el.focus()`), so Chromium matches `:focus-visible`.
- For each focused element, `getComputedStyle` assertions: `outlineStyle` is not `none`, `outlineWidth` is not `0px`, `outlineColor` is not `transparent` / `rgba(0, 0, 0, 0)`, and `el.matches(':focus-visible')` is `true`.
- Coverage: on `index.html` — text input, native `<button>`, `<textarea>`, `<select>`, checkbox, plain `<a>`; on `components.html` — `.nav a`, `.button`, and the aria-labelled `.icon-only` button.
- Three focus-state VRT baselines (`focus-input.png`, `focus-button.png`, `focus-link.png`) clipped around the focused element prove the focus ring visually. Baselines are darwin-only at this time and must be (re)generated after the `:focus-visible` CSS lands in `dist/`:
```bash
npx playwright test test/vrt/keyboard-focus.spec.js --project=chromium --update-snapshots
```

The spec is Chromium-pinned per the project's test contract and wired into `yarn test:vrt` via the `test:vrt` script in `package.json`.

### Full-page determinism: `index.html` `#embedded` (#180)

`index.html`'s `#embedded` section contains native `<audio controls>` / `<video controls>`, `<canvas>`, `<meter>`, `<progress>`, and a recursive `<iframe src="index.html">`. When scroll-stitched via `fullPage: true`, these elements render non-deterministically run-to-run (verified: 3 consecutive raw captures all differ byte-for-byte). None of them carry a Chota CSS contract (`.card` / `.tag` / `.button` / `.col-*`), so the index.html full-page tests inject `#embedded { visibility: hidden !important; }` via `page.addStyleTag` before the screenshot. This is a test-only runtime injection — `test/index.html` is not modified. The section's box is preserved (`visibility:hidden`, not `display:none`) so the page layout is unchanged. `components.html` has no such section and needs no injection.
Expand Down
119 changes: 0 additions & 119 deletions test/vrt/a11y-keyboard.spec.js

This file was deleted.

Loading
Loading