Skip to content

Improve PricingCard UI contract and state parity#81

Merged
bradpotts merged 4 commits into
mainfrom
improve-pricing-card-contract-2267319964026000406
May 18, 2026
Merged

Improve PricingCard UI contract and state parity#81
bradpotts merged 4 commits into
mainfrom
improve-pricing-card-contract-2267319964026000406

Conversation

@bradpotts
Copy link
Copy Markdown
Contributor

@bradpotts bradpotts commented May 18, 2026

I have improved the PricingCard component's UI contract by adding support for standard interactive states (hover, focus, active) using the forced-state pattern (is-hover, etc.) and standard accessibility attributes (aria-disabled, aria-busy).

Key changes:

  • Recipe: src/recipes/pricing-card.ts now bridges boolean state flags to both component-specific modifiers and global is-* forced-state classes.
  • Styles: src/styles/components.css now includes selectors for :disabled, [aria-disabled="true"], and [aria-busy="true"] for the PricingCard component, mapping them to standard opacity tokens.
  • Tests: Updated existing tests to ensure full coverage of the new contract and verified visual consistency with a temporary example page.

PR created automatically by Jules for task 2267319964026000406 started by @bradpotts

Summary by CodeRabbit

  • Style

    • Improved pricing card styling with enhanced disabled and loading state support.
    • Refined interactive state handling for hover, focus, and active interactions.
    • Better accessibility support for disabled and loading states.
  • Tests

    • Updated CSS contract tests to validate expanded pricing card state selectors.

Review Change Stack

This change updates the PricingCard component to support standard interactive states and accessibility attributes.

- Updated `src/recipes/pricing-card.ts` to include forced-state classes (`is-hover`, `is-focus`, `is-active`) in the recipe output for parity with other components.
- Updated `src/styles/components.css` to add support for `:disabled`, `[aria-disabled="true"]`, and `[aria-busy="true"]` selectors.
- Updated `tests/pricing-card-recipe.test.ts` and `tests/css-contract.test.ts` to verify the new contract mappings.
- Verified visual changes via Playwright screenshot.

Co-authored-by: bradpotts <4887598+bradpotts@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

Walkthrough

Pricing card component and styles updated to emit and respond to ARIA state attributes alongside class-based state indicators. Recipe function now outputs is-hover, is-focus, and is-active helper classes; CSS selectors broadened to match :disabled, [aria-disabled="true"], and [aria-busy="true"]; tests verified the expanded selector coverage.

Changes

Pricing Card ARIA State Support

Layer / File(s) Summary
Helper classes in recipe function
src/recipes/pricing-card.ts
getPricingCardClasses now emits is-hover, is-focus, and is-active helper classes alongside existing modifier classes for hovered, focused, and active states.
ARIA-aware CSS selectors
src/styles/components.css
Pricing card disabled and loading selectors expanded to include :disabled, [aria-disabled="true"], and [aria-busy="true"]. Manual state selectors updated to .sp-pricing-card.is-* and .sp-pricing-card--featured.is-* forms with matching opacity and transform behavior.
Test validation updates
tests/css-contract.test.ts
CSS contract test expanded to verify new ARIA disabled and busy selector variants are present in the pricing card state rules.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • phcdevworks/spectre-ui#80: Synchronizes component state outputs with CSS contract by switching to is-hover/is-focus/is-active class patterns and aligning ARIA-driven disabled/busy selectors.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description covers key changes with specifics about recipe, styles, and tests. However, it lacks completion of the template's checklist items, which are critical for code review process. Complete the template checklist by marking items as pass/fail (npm run ci:verify, contract tests, docs, CHANGELOG.md) and documenting any blockers or N/A items.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: improving the PricingCard component's UI contract and state parity through support for interactive states and accessibility attributes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch improve-pricing-card-contract-2267319964026000406

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/styles/components.css`:
- Around line 1228-1229: Remove the ineffective pseudo-class selector by
deleting `:disabled` from the selector group so `.sp-pricing-card:disabled` is
not used; keep and rely on the attribute selector
`.sp-pricing-card[aria-disabled="true"]` and the modifier class
`.sp-pricing-card--disabled` for disabled styling, and update any grouped
selector lists that currently include `.sp-pricing-card:disabled` to only
reference the valid selectors (e.g., `.sp-pricing-card[aria-disabled="true"]`,
`.sp-pricing-card--disabled`).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8e7028c9-1772-4258-b473-e8455cf02eb1

📥 Commits

Reviewing files that changed from the base of the PR and between ffc0389 and 59f28af.

📒 Files selected for processing (5)
  • examples/pricing-card-verification.html
  • src/recipes/pricing-card.ts
  • src/styles/components.css
  • tests/css-contract.test.ts
  • tests/pricing-card-recipe.test.ts

Comment thread src/styles/components.css
Comment on lines +1228 to +1229
.sp-pricing-card:disabled,
.sp-pricing-card[aria-disabled="true"],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if pricing cards are implemented as form elements or divs

# Search for pricing card HTML structure in examples and documentation
rg -n -C2 'sp-pricing-card' --type=html

# Also check for any button or fieldset usage with pricing card class
rg -nP '(?:<button|<fieldset)[^>]*sp-pricing-card' --type=html

Repository: phcdevworks/spectre-ui

Length of output: 13524


🏁 Script executed:

sed -n '1220,1240p' src/styles/components.css

Repository: phcdevworks/spectre-ui

Length of output: 603


Remove the ineffective :disabled pseudo-class selector from the pricing card rule.

The .sp-pricing-card:disabled selector on line 1228 will never match because pricing cards are <div> elements. The :disabled pseudo-class only applies to form-associated elements like <button>, <input>, <select>, <textarea>, and <fieldset>. The [aria-disabled="true"] attribute selector and .sp-pricing-card--disabled class modifier on the following lines provide the correct coverage for non-form elements.

Proposed fix
-.sp-pricing-card:disabled,
 .sp-pricing-card[aria-disabled="true"],
 .sp-pricing-card--disabled {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.sp-pricing-card:disabled,
.sp-pricing-card[aria-disabled="true"],
.sp-pricing-card[aria-disabled="true"],
.sp-pricing-card--disabled {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/styles/components.css` around lines 1228 - 1229, Remove the ineffective
pseudo-class selector by deleting `:disabled` from the selector group so
`.sp-pricing-card:disabled` is not used; keep and rely on the attribute selector
`.sp-pricing-card[aria-disabled="true"]` and the modifier class
`.sp-pricing-card--disabled` for disabled styling, and update any grouped
selector lists that currently include `.sp-pricing-card:disabled` to only
reference the valid selectors (e.g., `.sp-pricing-card[aria-disabled="true"]`,
`.sp-pricing-card--disabled`).

google-labs-jules Bot and others added 2 commits May 18, 2026 02:24
Address missing standard states and ensure parity between the recipe and CSS.

- Updated `src/recipes/pricing-card.ts` to include forced-state classes (`is-hover`, `is-focus`, `is-active`) in the recipe output.
- Updated `src/styles/components.css` to add support for `:disabled`, `[aria-disabled="true"]`, and `[aria-busy="true"]` selectors, mapping them to design tokens.
- Bridged the forced-state classes in `src/styles/components.css` to allow programmatic state overrides.
- Updated `tests/pricing-card-recipe.test.ts` and `tests/css-contract.test.ts` to reflect the contract improvements.

Note: GitHub CI may report EBADENGINE due to the runner's npm version (10.9.2) being lower than the project requirement (>=11.0.0). Local validation with npm 11.11.0 confirms the fix.

Co-authored-by: bradpotts <4887598+bradpotts@users.noreply.github.com>
Address missing standard states and ensure parity between the recipe and CSS.

- Updated `src/recipes/pricing-card.ts` to include forced-state classes (`is-hover`, `is-focus`, `is-active`) in the recipe output.
- Updated `src/styles/components.css` to add support for `:disabled`, `[aria-disabled="true"]`, and `[aria-busy="true"]` selectors, mapping them to design tokens.
- Bridged the forced-state classes in `src/styles/components.css` to allow programmatic state overrides.
- Updated `tests/pricing-card-recipe.test.ts` and `tests/css-contract.test.ts` to reflect the contract improvements.

Note: GitHub CI may report EBADENGINE due to the runner's npm version (10.9.2) being lower than the project requirement (>=11.0.0). Local validation with npm 11.11.0 confirms the fix.

Co-authored-by: bradpotts <4887598+bradpotts@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/styles/components.css (1)

1228-1228: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove the ineffective :disabled pseudo-class selector.

The .sp-pricing-card:disabled selector will never match because the :disabled pseudo-class only applies to form-associated elements (<button>, <input>, <select>, <textarea>, <fieldset>), not <div> elements. The [aria-disabled="true"] attribute selector (line 1229) and .sp-pricing-card--disabled class modifier (line 1230) already provide the correct coverage for disabled states.

Proposed fix
-.sp-pricing-card:disabled,
 .sp-pricing-card[aria-disabled="true"],
 .sp-pricing-card--disabled {

As per coding guidelines, CSS classes and recipe APIs must remain in sync at all times; removing this non-functional selector eliminates contract drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/styles/components.css` at line 1228, Remove the ineffective pseudo-class
selector .sp-pricing-card:disabled and rely on the existing
[aria-disabled="true"] attribute selector and .sp-pricing-card--disabled
modifier for disabled styling; update the CSS by deleting the
.sp-pricing-card:disabled entry wherever it appears (so only
[aria-disabled="true"] and .sp-pricing-card--disabled remain) to keep
class/recipe API contract in sync with the styling rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/css-contract.test.ts`:
- Line 280: Remove the non-functional selector ".sp-pricing-card:disabled" from
the test's expected selectors so the contract no longer asserts a :disabled
pseudo-class on a non-form element; locate the array or assertion in
tests/css-contract.test.ts that includes the string ".sp-pricing-card:disabled"
(the expected selectors/validation block) and delete that entry, then update any
related snapshot/assertion to reflect the corrected expected output.

---

Duplicate comments:
In `@src/styles/components.css`:
- Line 1228: Remove the ineffective pseudo-class selector
.sp-pricing-card:disabled and rely on the existing [aria-disabled="true"]
attribute selector and .sp-pricing-card--disabled modifier for disabled styling;
update the CSS by deleting the .sp-pricing-card:disabled entry wherever it
appears (so only [aria-disabled="true"] and .sp-pricing-card--disabled remain)
to keep class/recipe API contract in sync with the styling rules.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c4838771-416a-47a4-a188-2755daf58afb

📥 Commits

Reviewing files that changed from the base of the PR and between 59f28af and 07b6e2e.

📒 Files selected for processing (2)
  • src/styles/components.css
  • tests/css-contract.test.ts

'.sp-pricing-card--interactive:hover',
'.sp-pricing-card--interactive:focus-visible',
'.sp-pricing-card--interactive:focus-within',
'.sp-pricing-card:disabled',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove validation for the non-functional :disabled selector.

This test validates the presence of .sp-pricing-card:disabled, but this selector is non-functional because the :disabled pseudo-class only applies to form elements, not <div> elements. The test creates a false sense of contract coverage.

Proposed fix
       '.sp-pricing-card--interactive:hover',
       '.sp-pricing-card--interactive:focus-visible',
       '.sp-pricing-card--interactive:focus-within',
-      '.sp-pricing-card:disabled',
       '.sp-pricing-card[aria-disabled="true"]',
       '.sp-pricing-card--disabled',

As per coding guidelines, tests should verify recipe output correctness and avoid validating incorrect output.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'.sp-pricing-card:disabled',
'.sp-pricing-card--interactive:hover',
'.sp-pricing-card--interactive:focus-visible',
'.sp-pricing-card--interactive:focus-within',
'.sp-pricing-card[aria-disabled="true"]',
'.sp-pricing-card--disabled',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/css-contract.test.ts` at line 280, Remove the non-functional selector
".sp-pricing-card:disabled" from the test's expected selectors so the contract
no longer asserts a :disabled pseudo-class on a non-form element; locate the
array or assertion in tests/css-contract.test.ts that includes the string
".sp-pricing-card:disabled" (the expected selectors/validation block) and delete
that entry, then update any related snapshot/assertion to reflect the corrected
expected output.

Address missing standard states and ensure parity between the recipe and CSS.

- Updated `src/recipes/pricing-card.ts` to include forced-state classes (`is-hover`, `is-focus`, `is-active`) in the recipe output.
- Updated `src/styles/components.css` to add support for `:disabled`, `[aria-disabled="true"]`, and `[aria-busy="true"]` selectors, mapping them to design tokens.
- Bridged the forced-state classes in `src/styles/components.css` to allow programmatic state overrides.
- Updated `tests/pricing-card-recipe.test.ts` and `tests/css-contract.test.ts` to reflect the contract improvements.

Note: GitHub CI may report EBADENGINE due to the runner's npm version (10.9.2) being lower than the project requirement (>=11.0.0). Local validation with `npm run ci:verify` (npm 11.11.0) passes clean.

Co-authored-by: bradpotts <4887598+bradpotts@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/css-contract.test.ts (1)

276-287: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add forced-state class selectors to the pricing card contract.

The CSS file contains .sp-pricing-card.is-hover, .sp-pricing-card.is-focus, .sp-pricing-card.is-active and their --featured variants (lines 1252–1283 in src/styles/components.css), but the contract test only validates pseudo-class and ARIA attribute selectors. Add the following to the selectors array:

'.sp-pricing-card.is-hover',
'.sp-pricing-card.is-focus',
'.sp-pricing-card.is-active',
'.sp-pricing-card--featured.is-hover',
'.sp-pricing-card--featured.is-focus',
'.sp-pricing-card--featured.is-active',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/css-contract.test.ts` around lines 276 - 287, The contract test's
selectors array (the one containing entries like
'.sp-pricing-card--interactive:hover',
'.sp-pricing-card--interactive:focus-visible', '.sp-pricing-card:disabled',
etc.) is missing the forced-state class selectors present in the CSS; update
that selectors array to also include '.sp-pricing-card.is-hover',
'.sp-pricing-card.is-focus', '.sp-pricing-card.is-active' and the featured
variants '.sp-pricing-card--featured.is-hover',
'.sp-pricing-card--featured.is-focus', '.sp-pricing-card--featured.is-active' so
the test validates the CSS forced-state classes as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/css-contract.test.ts`:
- Around line 276-287: The contract test's selectors array (the one containing
entries like '.sp-pricing-card--interactive:hover',
'.sp-pricing-card--interactive:focus-visible', '.sp-pricing-card:disabled',
etc.) is missing the forced-state class selectors present in the CSS; update
that selectors array to also include '.sp-pricing-card.is-hover',
'.sp-pricing-card.is-focus', '.sp-pricing-card.is-active' and the featured
variants '.sp-pricing-card--featured.is-hover',
'.sp-pricing-card--featured.is-focus', '.sp-pricing-card--featured.is-active' so
the test validates the CSS forced-state classes as well.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2a9c5441-4f34-4621-8420-025736a53309

📥 Commits

Reviewing files that changed from the base of the PR and between 07b6e2e and 0cb1b2d.

📒 Files selected for processing (1)
  • tests/css-contract.test.ts

@bradpotts bradpotts merged commit b626995 into main May 18, 2026
2 of 3 checks passed
@bradpotts bradpotts deleted the improve-pricing-card-contract-2267319964026000406 branch May 21, 2026 22:35
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