Next Version Bump#2186
Conversation
Bumps [@pine-ds/icons](https://github.com/Kajabi/pine-icons) from 9.16.0 to 9.17.0. - [Release notes](https://github.com/Kajabi/pine-icons/releases) - [Changelog](https://github.com/Kajabi/pine-icons/blob/main/CHANGELOG.md) - [Commits](Kajabi/pine-icons@v9.16.0...v9.17.0) --- updated-dependencies: - dependency-name: "@pine-ds/icons" dependency-version: 9.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* style: replace hardcoded purple-600 with focus-ring token * style(breadcrumbs): add focus ring to progressbar variant
* feat(hero): add shadow prop to React component and SCSS * feat(hero): add shadow prop to Rails component * docs(hero): add shadow prop documentation and preview examples * fix(hero): quote 050 shadow key in SCSS map and fix alt text hyphenation
Removes Greet Standard as the primary heading font. The $-heading-font stack now leads with Inter, matching the direction set in ds-tokens FLEX-2659 (semantic heading tokens → inter). Language-specific overrides (Hebrew, Arabic, Devanagari) are unchanged. FLEX-2656
chore: configure Dependabot for GitHub Actions
* chore(claude): add review gauntlet skills and reviewer agents Introduces a sage-tailored review gauntlet modeled on the kajabi-products .claude/skills + .claude/agents pattern. Lineup: - sage-code-reviewer — React patterns (propTypes, forwardRef, classnames, configs.js), Jest spec coverage, story-file presence, XSS-via- dangerouslySetInnerHTML, conventional commit scoping - sage-design-reviewer — sage helper discipline (sage-color, sage-spacing, sage-border), Pine token alignment for dark-mode work, BEM-style classnames, SCSS structure, Storybook story coverage - sage-a11y-reviewer — keyboard, ARIA, focus management, semantic HTML, contrast (split from design so leaf-component a11y findings aren't diluted) - sage-existence-reviewer — duplication checks scoped to packages/sage- react/lib, packages/sage-assets/lib/stylesheets, and style-dictionary No security reviewer: sage-lib is a pure FE library with no auth surface; the only relevant check (XSS via dangerouslySetInnerHTML) folds into the code reviewer. sage-run-gauntlet orchestrates: identifies changed files, classifies them to determine which reviewers apply, launches the agents in parallel, then consolidates feedback with sequential numbering across BLOCKERS / SHOULD FIX / CONSIDER. Always applies the ran-gauntlet label to the PR. All skills and agents target develop and respect the "Do not Squash and Merge" convention so Lerna can build per-package changelogs. Used --no-verify because the lefthook commit-msg step (npx commitlint --edit) pulls @commitlint/cli@20.5.3, which throws "TypeError: format is not a function" on Node 22 regardless of message content. Sage-lib has @commitlint/cli@11.0.0 pinned locally; a follow-up PR can switch the hook to ./node_modules/.bin/commitlint to use the pinned version. * fix(claude): address Bugbot feedback on gauntlet skills Use yarn test:prod:react instead of nonexistent yarn jest; route token edits to design-only; add JSX className design review; scope existence to new artifacts and new token keys; include icon paths in checklist. * fix(claude): align gauntlet agents and lineup table with Step 2 Code reviewer runs lint and test:prod:react; existence table covers tokens and icons; a11y table excludes story-only diffs. * chore(lefthook): use local commitlint instead of npx in commit-msg (#2183) * chore(lefthook): use local commitlint instead of npx in commit-msg The `commit-msg` hook ran `npx commitlint --edit`, which fetches the latest from the npm registry every invocation. The current latest (@commitlint/cli@20.5.3) throws `TypeError: format is not a function` on Node 22 regardless of message content, blocking every commit and forcing contributors to use `--no-verify`. Switch to the locally-installed CLI pinned in yarn.lock. Lerna's hoisted layout doesn't create node_modules/.bin/commitlint at the repo root, so the binary is invoked directly via `node node_modules/@commitlint/cli/cli.js --edit`. Verified locally: a conventional message exits 0, a malformed message ("missing type") correctly fails with subject-empty + type-empty. This commit itself was authored through the patched hook (no --no-verify) as the proof. * chore(lefthook): trim commitlint hook comment * chore(lefthook): update commitlint hook comment * chore(claude): use sonnet for gauntlet reviewer agents * chore(claude): delegate code reviewer checks to sub-agents
… review notes (#2184) * chore(claude): classify sage-system in run-gauntlet + add sage-system review notes Running the gauntlet on sage-lib PR #2181 surfaced a classification gap: packages/sage-system/lib/*.js (vanilla-JS IIFE DOM helpers like Sage.popover and Sage.accordion) wasn't in sage-run-gauntlet's file-type rules. The closest existing rule, "React component files," scoped to packages/sage-react/lib/, didn't apply. I had to make a judgment call to route the PR to code-reviewer only. This adds: 1. An explicit sage-system rule in sage-run-gauntlet — route to code-reviewer only, skip a11y / design / existence, and skip the "missing spec" finding because sage-system has zero specs by repo convention. 2. A sage-system specifics section in sage-review-code — calls out that React criteria (propTypes, forwardRef, classnames, hooks, stories) do NOT apply, and lays out the BLOCKER / SHOULD FIX / not-applicable shape tuned to vanilla-JS DOM helpers. Cites the established defensive-pattern prior art in dropdown.js and tooltip.js so future reviewers anchor to it. 3. .claude/**/*.md added to the docs-only classification list in sage-run-gauntlet, mirroring the Pine fix in pine PR #745. Discovered while running the gauntlet on PR #2181 (fix(sage-system): null-guard popover + audit-driven cleanup). * chore(claude): add optional post-to-PR mode to sage-run-gauntlet (#2185) * chore(claude): add optional post-to-PR mode to sage-run-gauntlet Until now, sage-run-gauntlet output stayed in the conversation between the developer running it and the agent. That assumes the runner is also the PR author. When the gauntlet is run on someone else's PR — like the self-test against sage-lib PR #2181 — findings have nowhere to go that the original author can see. This adds a "Post results to the PR (optional)" section under After the Gauntlet: - Opt in when running on someone else's PR, when the user explicitly asks, or when findings include at least one BLOCKER / SHOULD FIX on an open PR - Opt out (default) for the runner's own pre-push self-review and for empty/clean results - Format: single `gh pr review --comment` body preserving sequential numbering and per-finding file:line refs; inline per-line comments would fragment the structured output - Fall back to `gh pr comment` if `gh pr review` 403s on collaborator scopes - Required footer line so human reviewers can tell it's machine- generated and find the criteria via the ran-gauntlet label Label-apply behavior unchanged: always run, regardless of post-or-not. * fix(claude): address Bugbot feedback on gauntlet post-to-PR mode Add ordered posting decision tree, skip when no PR exists, use --body-file with --repo on primary and fallback gh commands, and fix footer backticks in the heredoc template. * fix(claude): scope gauntlet PR fallback and add --repo to label Only fall back to gh pr comment on 403/404-style errors; fail loudly otherwise. Resolve PR_NUM once with --repo on label and post commands. * fix(claude): address round 3 Bugbot feedback on gauntlet post mode Distinguish gh pr view failures from missing PR, reorder posting rules so third-party CONSIDER findings post, align post body with Output Format, conditional footer via LABEL_APPLIED, and tighten fallback handling.
handleKeydown's `evt.target.closest('[data-js-popover]')` returns null
when keypresses bubble up from elements outside any popover, causing
`isExpanded(null)` to throw `Cannot read properties of null (reading
'classList')`. This is firing ~1.5k times/hour in Kajabi Admin RUM.
Also null-guards handleClick's `children[0]` lookup, removes a dead
`popoverPanel` local that was assigned but never read, and guards the
module-level `SELECTOR_LAST_FOCUSED` ref in closePopoverPanel for the
case where close fires without a prior open.
Audit of `closest()` / chained `querySelector()` usage in sage-system
turned up the same pattern (unguarded null deref) in 5 more places that
can throw `Cannot read properties of null` under transient DOM states.
- accordion-panel: guard `el.closest('.sage-expandable-card')`,
`el.closest(SELECTOR_ACCORDION)`, and the per-item header lookup.
- select: guard `evt.target.closest(elSelectClass)` in handleChange and
`el.querySelector('select')` in init.
- inputgroup: guard `parentElement` and `.sage-input__field` lookups in
togglePasswordDisplay; guard `closest()` results in addButtonPadding
and positionButtonOnError.
- tabs: guard `document.querySelector` results in eventHandlerChange
and dispatchChange; require `elPane.parentElement` before reading it.
- panel-controls: guard `closest()` results in updateBulkActionsToggleState
and handleExpandCollapseClick; guard `elRoot.querySelector` focus calls.
All early-return guards — no behavioral change when DOM is well-formed.
Per Bugbot review: the overlay-dismissal branch doesn't depend on elChild, so the early-return was wider than the path it protected. Moved the guard inside the trigger branch where elChild is actually dereferenced.
Guard parentNode check in handleClick and querySelector results in openPopoverPanel/closePopoverPanel to complete the audit cleanup.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fb35905. Configure here.
| // Shadow options — aligned with Pine pds-box shadow values | ||
| // Keep in sync with `packages/sage-react/lib/Hero/configs.js` | ||
| $-sage-hero-shadows: ( | ||
| none: 0, |
There was a problem hiding this comment.
Hero shadow "none" generates invalid CSS value
Medium Severity
The none key in $-sage-hero-shadows maps to the numeric value 0. When the @each loop interpolates this into box-shadow: #{$-val}, it produces box-shadow: 0; which is invalid CSS — the box-shadow shorthand requires at minimum two length values (offset-x and offset-y). Browsers will ignore the invalid declaration, so the shadow="none" option won't actually reset any inherited box-shadow. The value for none needs to be the CSS keyword none (not the number 0) to generate valid box-shadow: none;.
Reviewed by Cursor Bugbot for commit fb35905. Configure here.
| // Keep in sync with `packages/sage-react/lib/Hero/configs.js` | ||
| $-sage-hero-shadows: ( | ||
| none: 0, | ||
| "050": sage-shadow("050"), |
There was a problem hiding this comment.
Sass type mismatch breaks "050" shadow variant lookup
Medium Severity
sage-shadow("050") passes a quoted string "050" to map-get, but in the $sage-shadows map the key 050 is parsed by Sass as the number 50. Sass map-get requires both type and value to match, so string "050" ≠ number 50 returns null. This makes .sage-hero--shadow-050 emit an empty box-shadow value — the 050 shadow variant silently does nothing. The other entries like sage-shadow(100) work because both sides are numbers.
Reviewed by Cursor Bugbot for commit fb35905. Configure here.


Note
Low Risk
Mostly additive API (Hero shadow), visual/token tweaks, and defensive JS; no auth or data-path changes, though broad focus-ring updates affect many components visually.
Overview
This release bundles design-system product changes with Claude review automation and small repo hygiene.
Hero
shadowprop — React (Hero.jsx,configs.js), SCSS (_hero.scssBEM modifiers aligned with Pine box-shadow steps), Storybook/docs, and sage-rails (SageHero, tokens, preview/props) all gain optional shadow values (none,050–500).Pine token / a11y styling — Many component SCSS files swap focus-ring color from
--pine-color-purple-600to--pine-color-focus-ring; avatar surfaces move to semantic Pine background tokens; progress-bar breadcrumbs getsage-focus-ring; heading stack drops Greet Standard in favor of Inter only.sage-system — Accordion, input group, panel controls, popover, select, and tabs gain null-guards on DOM lookups to avoid throws when markup is missing.
Tooling — New
.claudeagents (code, design, a11y, existence) and skills includingsage-run-gauntletfor parallel pre-PR review; Dependabot adds weekly github-actions grouping; CODEOWNERS for.github/; lefthook runs pinned local commitlint instead ofnpx;@pine-ds/iconsbumps in the lockfile.Reviewed by Cursor Bugbot for commit fb35905. Bugbot is set up for automated code reviews on this repo. Configure here.