Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
a02bc8e
feat(pixel-utils): add isolated styling instances
toxsick Oct 14, 2026
dd23826
feat(pixel-utils): ship owned semantic token stylesheet
toxsick Oct 14, 2026
e0f2f40
feat(uniform): expose stable editable control associations
toxsick Oct 14, 2026
a670f57
fix(uniform): keep cleared numeric inputs empty
toxsick Oct 14, 2026
f369a06
style(uniform): separate adjacent test cases
toxsick Oct 14, 2026
144f419
fix(storybook-config): isolate browser dependency optimization
toxsick Oct 14, 2026
463d133
style(pixels): separate adjacent test cases
toxsick Oct 14, 2026
e90bbcd
style(pixel-utils): separate adjacent styling tests
toxsick Oct 14, 2026
e4f0756
fix(uniform): cancel pending edits when fields reset
toxsick Oct 14, 2026
c2a07fa
feat(uniform): prototype owned React Aria input composition
toxsick Oct 14, 2026
b672a9b
feat(uniform): migrate production inputs to owned React Aria controls
toxsick Oct 14, 2026
e17557b
fix(storybook-config): compile Tailwind in browser tests
toxsick Oct 14, 2026
9428548
feat(uniform): migrate textarea to owned field rendering
toxsick Oct 14, 2026
dd07fe5
feat(uniform): own React Select field integration
toxsick Oct 14, 2026
3001aa5
feat(uniform): migrate switch to one owned control
toxsick Oct 14, 2026
925a20e
feat(uniform): migrate array and card group presentation
toxsick Oct 14, 2026
f192235
feat(uniform): migrate switch box to one owned control
toxsick Oct 14, 2026
4570953
feat(uniform): migrate owned radio fields and tab selection
toxsick Oct 14, 2026
3a3ea4e
feat(uniform): migrate owned checkbox groups and option composition
toxsick Oct 15, 2026
29022c8
feat(uniform): migrate slider to one owned range control
toxsick Oct 15, 2026
4b4f542
feat(uniform): migrate owned segmented time field
toxsick Oct 15, 2026
923d917
feat(uniform): migrate owned date picker and calendar
toxsick Oct 15, 2026
69460e1
feat(uniform): migrate remaining styling consumers
toxsick Oct 15, 2026
09711a4
refactor(uniform)!: remove legacy HeroUI hook APIs
toxsick Oct 15, 2026
b51dc67
refactor(uniform): separate input binding and presentation
toxsick Oct 15, 2026
f343b2e
refactor(uniform): clarify input handlers and variant types
toxsick Oct 15, 2026
b931d14
docs(uniform): explain input subcomponents and link Aria docs
toxsick Oct 15, 2026
d70b325
test(uniform): consolidate input behavior coverage
toxsick Oct 15, 2026
2bb9b79
refactor(uniform): consolidate input stories and colocate props
toxsick Oct 15, 2026
9c30c82
refactor(uniform): simplify field composition and document conventions
toxsick Oct 16, 2026
88edf8d
refactor(uniform): separate textarea binding and presentation
toxsick Oct 16, 2026
06587a1
refactor(uniform): isolate segmented time presentation
toxsick Oct 16, 2026
b331d3e
refactor(uniform): split date input and calendar presentation
toxsick Oct 16, 2026
b7fcd5b
refactor(uniform): separate radio group binding and presentation
toxsick Oct 16, 2026
9a728f1
refactor(uniform): colocate radio box controls and variants
toxsick Oct 16, 2026
eb3b28a
refactor(uniform): isolate checkbox group and option controls
toxsick Oct 16, 2026
8dd350d
refactor(uniform): separate switch binding and presentation
toxsick Oct 16, 2026
7867a38
refactor(uniform): isolate switch box presentation
toxsick Oct 16, 2026
e6d6997
fix(tailwind-config): scan TypeScript styling recipes
toxsick Oct 16, 2026
b5b982e
refactor(uniform): simplify field recipes and state composition
toxsick Oct 16, 2026
20dfce5
refactor(uniform): separate select binding and engine adapters
toxsick Oct 16, 2026
d7eaacd
refactor(uniform): isolate slider presentation and track controls
toxsick Oct 16, 2026
62180e5
refactor(uniform): separate tab selection binding and presentation
toxsick Oct 16, 2026
3acbe53
refactor(uniform): separate card validation and presentation
toxsick Oct 16, 2026
80fe707
refactor(uniform): separate array binding and row presentation
toxsick Oct 16, 2026
52f7dac
docs(uniform): document field renderer ownership
toxsick Oct 16, 2026
71dcd75
refactor(uniform): use canonical radio box styling props
toxsick Oct 16, 2026
171c88c
chore: improve readability
toxsick Oct 16, 2026
c7d746f
refactor(pixels,uniform,megapixels): group and order slot styles
toxsick Oct 16, 2026
66decfd
refactor(uniform): consolidate field bindings and simplify controls
toxsick Oct 16, 2026
45b243e
refactor(uniform): simplify field controls and unify layout composition
toxsick Oct 16, 2026
457bbcf
refactor(pixels): migrate Button and ButtonGroup off HeroUI
toxsick Oct 17, 2026
fcfcac7
refactor(pixels): replace HeroUI Popover with React Aria
toxsick Oct 17, 2026
f893840
refactor(pixels): replace HeroUI ScrollShadow with owned scrolling
toxsick Oct 17, 2026
7d7f2fc
refactor(pixels): replace HeroUI Progress with owned rendering
toxsick Oct 17, 2026
ca26011
refactor(pixels): replace HeroUI circular progress with owned rendering
toxsick Oct 17, 2026
f2483ee
refactor(pixels): replace HeroUI Tooltip with React Aria
toxsick Oct 17, 2026
a8e4228
refactor(storybook-config): organize stories by owner and scenario
toxsick Oct 17, 2026
e5c11d2
refactor(pixels): replace HeroUI Menu with React Aria
toxsick Oct 17, 2026
9248000
refactor(pixels): replace HeroUI Modal with React Aria
toxsick Oct 17, 2026
2249004
refactor(pixels): replace HeroUI Drawer with shared dialog engine
toxsick Oct 17, 2026
a1457b8
refactor(pixels): replace HeroUI Avatar with owned rendering
toxsick Oct 17, 2026
1a6a130
refactor(pixels): replace HeroUI AvatarGroup with owned composition
toxsick Oct 17, 2026
3e484c7
refactor(pixels): replace HeroUI Badge with owned rendering
toxsick Oct 17, 2026
4571133
refactor(pixels): replace HeroUI Label with owned rendering
toxsick Oct 17, 2026
792bf33
refactor(pixels): replace HeroUI Accordion with owned rendering
toxsick Oct 17, 2026
56e090e
refactor(pixels): replace HeroUI Alert with owned rendering
toxsick Oct 17, 2026
6f63d8e
refactor(pixels): replace HeroUI Breadcrumb with owned rendering
toxsick Oct 17, 2026
eaff96d
refactor(pixels): replace HeroUI Card with owned rendering
toxsick Oct 17, 2026
1bd36f0
refactor(pixels): replace HeroUI SearchInput with owned rendering
toxsick Oct 17, 2026
b4e623c
refactor(pixels): replace HeroUI Tabs with owned rendering
toxsick Oct 17, 2026
efeb09e
refactor(pixels): replace HeroUI Table with owned rendering
toxsick Oct 17, 2026
e89fb11
refactor(pixel-utils)!: remove remaining HeroUI theme integration
toxsick Oct 17, 2026
6c0d613
refactor(tailwind-config): use CSS-first Tailwind 4 configuration
toxsick Oct 17, 2026
302dc78
fix(uniform): omit disabled values before schema validation and submi…
toxsick Oct 24, 2026
d716e37
chore(uniform): remove docs links from Readme
toxsick Oct 24, 2026
f9ae9a8
Merge pull request #1928 from fuf-stack/review/disabled-field-submit-…
toxsick Oct 24, 2026
dd301f1
chore(security): harden snapshot autofix and fix sensitive data redac…
toxsick Oct 24, 2026
130b182
chore(deps): use Renovate best practices preset
toxsick Oct 24, 2026
2c88271
chore(deps): update dependency @tsdown/css to v0.23.1 (#1929)
renovate[bot] Oct 24, 2026
b2d0756
chore: merge main and resolve security audit documentation conflict
toxsick Oct 24, 2026
5fdac78
chore(deps): update dependency tsdown to v0.23.1 (#1930)
renovate[bot] Oct 24, 2026
f8c969c
chore(ci): match coverage sources across project roots
toxsick Oct 24, 2026
352be0e
chore(security): address snapshot autofix review findings
toxsick Oct 24, 2026
8f48978
Merge pull request #1931 from fuf-stack/review/audit-security
toxsick Oct 24, 2026
b9ea4c1
chore: merge main into review/remove-heroui
toxsick Oct 24, 2026
a333632
fix(pixels): preserve React 18 support after HeroUI migration
toxsick Oct 24, 2026
ff21258
chore: merge next into review/remove-heroui
toxsick Oct 24, 2026
7392fa5
test(pixels): allow asynchronous overlay focus restoration
toxsick Oct 24, 2026
c88b891
test(pixels): establish modal keyboard opener before dismissal
toxsick Oct 24, 2026
a5e0540
test: serialize Storybook files to avoid browser focus contention
toxsick Oct 24, 2026
8521beb
docs: replace migration artifacts with verification summary
toxsick Oct 24, 2026
194554c
feat(global): identify component roots with data-component markers
toxsick Oct 24, 2026
5338054
refactor(megapixels): align component structure, styling and document…
toxsick Oct 25, 2026
8058e28
docs(uniform): restore useful field and binding descriptions
toxsick Oct 25, 2026
25091da
refactor(megapixels): replace Base UI checkbox with React Aria
toxsick Oct 25, 2026
cb96a6d
fix(storybook-config): order scenario groups last and explain edge cases
toxsick Oct 25, 2026
790ed2c
test(storybook-config): add story-driven visual regression pilot
toxsick Oct 25, 2026
1c7bc47
test(global): add dedicated test category commands
toxsick Oct 25, 2026
aa2d107
test(global): parallelize Storybook and unify test reporting
toxsick Oct 25, 2026
7d55196
fix(global): clean up field warnings and expected story errors
toxsick Oct 25, 2026
f25daf3
test(global): expand visual coverage across UI packages
toxsick Oct 25, 2026
71cdeba
test(global): include visual tests in full test runs
toxsick Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
56 changes: 49 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ jobs:
- uses: ./.github/actions/setup-project
- name: Validate dependency boundaries
run: pnpm check:boundaries
- name: Verify release bootstrap
run: pnpm test:release
- name: Lint packages
run: pnpm lint

Expand All @@ -44,8 +42,8 @@ jobs:
- uses: ./.github/actions/setup-project
- name: Run unit tests with coverage
# Nested project names begin with the config package, not "storybook".
# Vitest 5 resolves coverage globs relative to each selected package.
run: pnpm test --project='!@repo/storybook-config*' --coverage.include='src/**/*.{js,jsx,ts,tsx}'
# Match source paths relative to either a package or the root CI project.
run: pnpm test:unit --coverage.include='**/src/**/*.{js,jsx,ts,tsx}'
- name: Verify coverage report
run: |
test -s coverage/lcov.info
Expand All @@ -58,10 +56,15 @@ jobs:
path: coverage/
if-no-files-found: error

storybook:
name: Storybook tests
storybook-shards:
name: Storybook tests (${{ matrix.shard }}/4)
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
# Each shard gets its own browser and runner, preserving focus isolation.
fail-fast: false
matrix:
shard: [1, 2, 3, 4]

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
Expand All @@ -71,7 +74,46 @@ jobs:
# binary and Linux system dependencies before running the browser tests.
run: pnpm --filter @repo/storybook-config exec playwright install --with-deps chromium
- name: Run Storybook browser tests
run: pnpm test:storybook
run: pnpm test:storybook --shard=${{ matrix.shard }}/4

storybook:
# Preserve the existing status check name for branch protection.
name: Storybook tests
needs: [storybook-shards]
if: ${{ always() }}
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Verify all Storybook shards passed
run: test "${{ needs.storybook-shards.result }}" = success

visual-tests:
name: Visual regression tests
runs-on: ubuntu-24.04-arm
timeout-minutes: 20
container:
image: mcr.microsoft.com/playwright:v1.64.0-noble@sha256:06a9939e57531807f8d5fd76ce44b53165ffb7d7501d87ab10e285c20b1e971f
options: --ipc=host

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/setup-project
- name: Build Storybook
run: pnpm --filter @repo/storybook-config storybook:build
- name: Compare visual baselines
env:
FORCE_COLOR: '1'
run: pnpm test:visual
- name: Upload visual report and differences
if: ${{ !cancelled() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: visual-regression-report
path: |
packages/config/storybook-config/playwright-report/
packages/config/storybook-config/test-results/visual/
retention-days: 14
if-no-files-found: error

package-smoke:
name: Package smoke tests
Expand Down
218 changes: 93 additions & 125 deletions .github/workflows/renovate-snapshot-autofix.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,38 @@
# This workflow lets maintainers explicitly request snapshot autofixes on Renovate PRs.
# It runs tests, updates snapshots only when failures are snapshot-related, and pushes
# the snapshot commit back to the Renovate branch while blocking non-snapshot changes.
# It tests updates and pushes external snapshot changes back to the Renovate branch.
# Untrusted build/test code and repository write credentials run on separate runners.
name: renovate-snapshot-autofix

on:
# Request-driven mode on Renovate PRs.
# We include synchronize/reopened so reruns happen automatically after updates
# when the label is still present.
# Request-driven mode on Renovate PRs. Keep rerunning after updates/reopens
# while the opt-in label is present.
pull_request_target:
types:
- labeled
- synchronize
- reopened
types: [labeled, synchronize, reopened]
# Alternative request mode: comment `/autofix-snapshots` on a Renovate PR.
issue_comment:
types:
- created
types: [created]
# Fallback manual trigger from Actions UI for a specific branch.
workflow_dispatch:
inputs:
branch:
description: 'Renovate branch to update snapshots for (e.g. renovate/pkg-1.x)'
description: 'Renovate branch to update external snapshots for'
required: true
type: string

permissions:
# Needed to dispatch CI after the GITHUB_TOKEN-backed push. Such pushes do not
# emit another pull_request event themselves.
actions: write
# Needed to push snapshot updates back to the Renovate branch.
contents: write
# Needed to read PR metadata (label + head branch).
pull-requests: read
# Grant each job only the permissions needed for its part of the workflow.
permissions: {}

jobs:
autofix-snapshots:
name: Autofix Snapshot Updates
resolve:
name: Resolve immutable Renovate head
runs-on: ubuntu-latest
timeout-minutes: 20
# Run when:
# 1) a Renovate PR has the opt-in label,
# 2) a maintainer comments `/autofix-snapshots` on a PR, or
# 3) the workflow is manually dispatched.
timeout-minutes: 5
permissions:
contents: read
# Needed to resolve current PR metadata for label and comment requests.
pull-requests: read
# Run for an opted-in same-repository Renovate PR, an authorized comment,
# or a manual request. Resolve the head once so later jobs test the same SHA.
if: >-
${{
(github.event_name == 'pull_request_target' &&
Expand All @@ -54,113 +45,90 @@ jobs:
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
github.event_name == 'workflow_dispatch'
}}

outputs:
branch: ${{ steps.head.outputs.branch }}
sha: ${{ steps.head.outputs.sha }}
steps:
- name: Resolve PR head branch for issue comment trigger
id: pr-head
if: github.event_name == 'issue_comment'
# Resolve requests using the trusted workflow revision, without Git auth.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.workflow_sha }}
persist-credentials: false
- id: head
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { owner, repo } = context.repo;
const issueNumber = context.payload.issue.number;
const { data: pr } = await github.rest.pulls.get({
owner,
repo,
pull_number: issueNumber,
});

if (!pr.head.ref.startsWith('renovate/')) {
core.setFailed(`PR head '${pr.head.ref}' is not a renovate branch.`);
return;
}
if (pr.head.repo.full_name !== `${owner}/${repo}`) {
core.setFailed(
`Refusing to push to external repo '${pr.head.repo.full_name}'.`,
);
return;
}

core.setOutput('ref', pr.head.ref);
core.setOutput('repository', pr.head.repo.full_name);

const { pathToFileURL } = require('node:url');
const scriptUrl = pathToFileURL(`${process.env.GITHUB_WORKSPACE}/scripts/ci-snapshot-autofix.mjs`);
const { resolveHead } = await import(scriptUrl.href);
await resolveHead({ github, context, core });

snapshots:
name: Build and test without write credentials
# Branch code must not populate trusted caches, including on manual runs.
cache-mode: read
needs: resolve
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
outputs:
changed: ${{ steps.payload.outputs.changed }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
# For label-triggered runs, check out the Renovate PR head branch.
# For issue comments, resolve PR head via API first.
# For manual runs, use the branch provided as workflow input.
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.ref || github.event_name == 'issue_comment' && steps.pr-head.outputs.ref || github.event.inputs.branch }}
# For Renovate PRs this can point to a fork; for manual runs use current repo.
repository: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name || github.event_name == 'issue_comment' && steps.pr-head.outputs.repository || github.repository }}

# Build the resolved PR/manual-request commit without retaining auth.
ref: ${{ needs.resolve.outputs.sha }}
persist-credentials: false
- name: Setup project
uses: ./.github/actions/setup-project

- name: Run tests
id: test-run
shell: bash
run: |
set +e
pnpm test
test_exit=$?
set -e

if [ "$test_exit" -eq 0 ]; then
echo "status=passed" >> "$GITHUB_OUTPUT"
exit 0
fi

echo "status=failed" >> "$GITHUB_OUTPUT"

- name: Update snapshots
if: steps.test-run.outputs.status == 'failed'
run: pnpm test -u

- name: Ensure only snapshot files changed
if: steps.test-run.outputs.status == 'failed'
shell: bash
run: |
changed_files="$(git status --porcelain=v1 --untracked-files=all | cut -c4-)"
if [ -z "$changed_files" ]; then
echo "Tests failed, but updating snapshots produced no changes."
exit 1
fi

# Safety gate: only allow known snapshot file patterns.
non_snapshot_files="$(printf '%s\n' "$changed_files" | rg -v '(^|/)__snapshots__/|\.snap$|(\.|/)(test|spec)\.(ts|tsx|js|jsx|mjs|cjs)$' || true)"
if [ -n "$non_snapshot_files" ]; then
echo "Found non-snapshot changes after update:"
printf '%s\n' "$non_snapshot_files"
exit 1
fi

- name: Verify tests pass with updated snapshots
if: steps.test-run.outputs.status == 'failed'
run: pnpm test

- name: Commit and push snapshot updates
id: snapshot-commit
if: steps.test-run.outputs.status == 'failed'
shell: bash
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "test: update snapshots"
git push
echo "pushed=true" >> "$GITHUB_OUTPUT"

- name: Run CI for the snapshot commit
if: steps.snapshot-commit.outputs.pushed == 'true'
- name: Run tests and collect external snapshot updates
id: payload
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { pathToFileURL } = require('node:url');
const scriptUrl = pathToFileURL(`${process.env.GITHUB_WORKSPACE}/scripts/ci-snapshot-autofix.mjs`);
const { buildSnapshots } = await import(scriptUrl.href);
buildSnapshots({ core });
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
if: steps.payload.outputs.changed == 'true'
with:
name: snapshots-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/snapshot-output/snapshots.json
if-no-files-found: error
retention-days: 1

write:
name: Apply validated snapshot data
needs: [resolve, snapshots]
if: needs.snapshots.outputs.changed == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
# Needed to create the snapshot commit and update the Renovate branch.
contents: write
# GITHUB_TOKEN-backed commits do not emit another pull_request event;
# dispatch CI explicitly after the snapshot commit is accepted.
actions: write
steps:
# Never check out the Renovate head or install its dependencies here.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.workflow_sha }}
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
with:
name: snapshots-${{ github.run_id }}-${{ github.run_attempt }}
path: snapshot-input
- name: Validate snapshots and create commit through GitHub API
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
HEAD_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.ref || github.event_name == 'issue_comment' && steps.pr-head.outputs.ref || github.event.inputs.branch }}
HEAD_BRANCH: ${{ needs.resolve.outputs.branch }}
HEAD_SHA: ${{ needs.resolve.outputs.sha }}
with:
script: |
await github.rest.actions.createWorkflowDispatch({
...context.repo,
workflow_id: 'ci.yml',
ref: process.env.HEAD_REF,
});
const { pathToFileURL } = require('node:url');
const scriptUrl = pathToFileURL(`${process.env.GITHUB_WORKSPACE}/scripts/ci-snapshot-autofix.mjs`);
const { run } = await import(scriptUrl.href);
await run({ github, context });
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ node_modules
# Extentions
*.pyc
*.csv*

# Storybook builds and visual-test artifacts
storybook-static/
playwright-report/
test-results/
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
Pixels is a pnpm monorepo of independently released TypeScript packages. Keep
changes focused and read the nearest `AGENTS.md` before editing a package.

## Code style

Read `docs/code-style.md` before component changes. It records readability,
component/type ownership, documentation, test and story conventions that lint
cannot fully enforce. Follow `docs/styling.md` for styling implementation details.
When the maintainer gives additional agreed code-style feedback, update the
relevant guide rule and its concrete example in the same change, and reconcile
cross-referenced documentation. Preserve the user's current review/commit flow.

## Architecture

- Foundations: `pixel-utils`, `pixel-motion`, and the independent validation
Expand Down
Loading
Loading