* feat(top-navigation): apply font scaling with component-level clamp bounds (#1477)
* feat(top-navigation): apply font scaling with component-level clamp bounds
- Wrap title/subtitle in clamp() with per-layout min/max font-size and line-height scale bounds (nested with global limits; component range narrows global)
- Introduce main/left/right slot tokens for inter-slot spacing; tokenize android 16px left padding
- Rename root.minHeight to root.height
- Switch recipe to typed tokens.$fontSize.tNStatic / tokens.$lineHeight.tNStatic references
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(top-navigation): drop spacing slot tokens, keep only font scaling
- Revert main/left/right slot additions from rootage; title/subtitle scale tokens kept
- Restore cupertino main padding to raw var(--centered-title-padding-x, 0)
- Restore android left paddingRight hardcoded 16px
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: changeset
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(top-navigation): raise min font/line-height scale to 1
Prevent downscaling of title and subtitle under user font size settings by
raising minFontSizeScale and minLineHeightScale from 0.8 to 1. Only the
upper clamp (1.2x) remains active.
* docs(list): update Align Property description (#1494)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore(deps): update dependency bun to v1.3.12 (#1468)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 정현수 <hyeonsu.github@gmail.com>
* chore(deps): update anthropics/claude-code-action digest to 905d4eb (#1467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 정현수 <hyeonsu.github@gmail.com>
* docs
* feat(docs): add deprecation notices and replacement links to llms.txt (#1474)
* feat(docs): add deprecation notices and replacement links to llms.txt
Deprecated components now surface their deprecation message and a link
to the replacement component in llms.txt output. Adds `replacement`
frontmatter field to all deprecated MDX files and fills missing
`deprecated` fields on 8 React component docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): use options object for getLLMTextForFullCompilation to fix type error
The positional optional params conflicted with Array.map's (value, index, array)
signature in breeze/lynx/ai-integration routes. Using an options object avoids this.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): simplify getLLMTextForFullCompilation to remove Section dependency
Keep replacement links only in individual page output (getLLMText).
Full compilation output now shows only the deprecated notice without
replacement links, eliminating the need for Section/allPages params
and fixing the .map() signature conflict with other routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): remove unnecessary lambda wrapper in llms-full routes
pages.map(getLLMTextForFullCompilation) works directly since the
function takes a single parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): revert formatting-only changes in llms-full routes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): address PR review feedback for llms.txt deprecation
- Fix replacement slugs: hstack→h-stack, vstack→v-stack to match actual page slugs
- Sort llms.txt index by displayTitle instead of full string with (Deprecated) suffix
- Unify deprecation notice logic into buildDeprecationNotice with optional params
- Remove unnecessary titleToSlug since replacement values are already slugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docs): add missing description field to deprecated MDX files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "fix(docs): add missing description field to deprecated MDX files"
This reverts commit 5fdd4d6.
* refactor(docs): remove replacement field, embed markdown links in deprecated text
Instead of a separate `replacement` slug field with matching logic,
embed markdown links directly in the `deprecated` frontmatter text.
This simplifies the codebase by removing the replacement field from
schema, types, and the slug-matching logic in get-llm-text.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(docs): render markdown links in deprecated frontmatter text
Fumadocs does not render markdown in frontmatter fields by default.
Add renderInlineMarkdown helper to convert [text](url) patterns in
deprecated messages into clickable Next.js Link components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(docs): move deprecation message to MDX body as Callout
Unify UI and llms.txt deprecation rendering through a single source.
Change `deprecated` frontmatter from message string to boolean flag, and
move the actual guidance (with markdown link to the replacement) into
each MDX body as a Callout. Fumadocs renders the Callout natively for
the UI, and the processed body flows into llms.txt with the markdown
link intact, so LLMs can follow it to the replacement's llms.txt.
The boolean flag now drives all structural branches (title suffix,
sidebar badge, index label, component-grid filtering), while the body
Callout carries the human-readable message. This removes the manual
renderInlineMarkdown helper in page.tsx and the buildDeprecationNotice
helper in get-llm-text.ts.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(deprecated): align Callout guidance with rootage deprecation field
Make each deprecated component's Callout match its rootage `deprecated`
message so the UI/llms.txt guidance agrees with the canonical source.
- action-chip: point to Chip.Button with variant="solid"
- control-chip: point to Chip.Toggle or Chip.Button
- link-content: point to Action Button with variant="ghost"
- fab: point to contextual-floating-button (was floating-action-button)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(deprecated): add "Deprecated" title to deprecation Callouts
Add `title="Deprecated"` to the first Callout of every deprecated
component MDX so the notice is visually heavier — the title renders as
a bold label above the message, making it easier to spot the warning
at a glance.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(docs): use gray-matter for frontmatter parsing
Replace naive regex-based frontmatter parser with gray-matter, which
correctly types YAML boolean values. This also fixes the CLI `docs`
command not surfacing deprecation hints — the old parser stored all
values as strings, so `frontmatter.deprecated === "true"` happened
to work with the previous string-message schema but broke after the
schema changed to a proper boolean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(docs): drop gray-matter, extend naive parser to strip quotes
Reverts the gray-matter dependency added in df01f9f. The naive regex
parser already handles this script's needs once the frontmatter.deprecated
comparison is kept as the raw string "true" — which matches the boolean
YAML value the parser captures as text.
Adds quote-stripping so YAML values that must be quoted (e.g. descriptions
starting with `@`) parse cleanly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(docs): replace manual frontmatter parsing with gray-matter (#1508)
Use gray-matter in generate-docs-index and import-components-to-sanity
scripts instead of custom regex-based parsers. Boolean frontmatter
values (e.g., deprecated) are now parsed as real booleans, removing
the need for string comparisons.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ci): unblock claude auto-fix in sync-figma-entities workflow (#1512)
The build step's `bunx tsc --noEmit | tee` line died on the pipe under
`set -e -o pipefail` before reaching the `tsc_failed=true` output write,
which silently disabled the claude-fix step (its `if` guards on that
output). Wrap the pipe in `if !` so the failure is captured explicitly
without tripping `set -e`, allowing the auto-fix flow to actually run.
* fix(ci): build slack payload with jq to escape claude summary safely
The Slack notification step interpolated claude-summary's free-form
korean text directly into a YAML mrkdwn string, which broke the payload
parser whenever the summary contained quotes, colons, or newlines (the
exact failure on run #24819145934). Build the JSON payload in a prior
bash+jq step where --arg auto-escapes the summary, then hand it to
slack-github-action via payload-file-path.
Also fixes a latent bug: the inline `format('\\n...')` produced a
literal `\n` (two chars), so summaries never actually line-broke in
mrkdwn. printf '%s\\n%s' now produces a real newline that jq encodes
into a proper JSON `\\n`.
* feat(notification-badge): add min width/height/aspect-ratio for size=large (#1500)
* feat(notification-badge): add min width/height/aspect-ratio for size=large
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(notification-badge): add Positioner usage story
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(notification-badge): simplify Positioner story condition map
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(top-navigation): separate button rootage declarations (#1516)
* chore(cli): Add PostHog docs env wiring and CLI outcome telemetry (#1513)
* feat: add PostHog telemetry for docs and CLI outcomes
* chore(cli): align telemetry log messaging
* chore(deps): update dependency bun to v1.3.13 (#1496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* feat(docs): transform ProgressBoardTable in llms.txt output (#1519)
* feat(docs): transform ProgressBoardTable in llms.txt output
Adds a normalize rule that replaces <ProgressBoardTable /> with markdown
summary and per-component status tables sourced from Sanity, mirroring
the existing platformStatusRule pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(docs): cover progressBoardRule with fixtures and guard empty status
- Skip link rendering when status is missing to avoid misleading "Not Ready" link
- Add fixture-based happy-path and 0/0 not-planned edge-case tests
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(snackbar): change default duration from 5s to 4s (#1517)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: update changeset
* feat(snackbar): add immediate/queued strategy for multi-snackbar behavior (#1413)
* test(snackbar): add useSnackbar tests for immediate/queued strategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(snackbar): add immediate/queued strategy for multi-snackbar behavior
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(snackbar): add strategy example and stackflow-spa queued demo
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(snackbar): add richer mixed strategy test cases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(snackbar): use dismissing state for immediate replacement to preserve animations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(snackbar): add dismiss+setTimeout workaround demo to stackflow-spa
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(snackbar): simplify strategy example with positive/critical variants
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(snackbar): use functional setState to prevent stale closure race
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(snackbar): use switch statement for state machine entry events
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(snackbar): wrap strategy example with SnackbarProvider
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(snackbar): migrate state machine to useReducer for atomic transitions
Fixes two real bugs that arose from mixing side-effects into setState
updaters and splitting onClose invocation across event handler and effect:
- StrictMode double-invocation of setState updaters caused pop() (which
itself mutated queue via setQueue) to run twice, corrupting the queue
on rapid synchronous pushes.
- dismiss() invoked onClose directly while the dismissing effect also
invoked it after removeDelay, resulting in duplicate calls.
Consolidates state, queue, and currentSnackbar into a single reducer so
all transitions are pure. Timers and onClose now live only in the effect.
events memo drops its state dependency as a side benefit.
* docs(snackbar): annotate dismiss+setTimeout workaround example
Adds a brief comment explaining the pattern and makes the setTimeout
delay explicit (0) so readers don't have to infer intent from an omitted
argument.
* test(snackbar): lock in onClose semantics for dropped queue items
Pins the intended contract of onClose: it fires only for snackbars that
were actually presented (became currentSnackbar). Queued items cleared
by an immediate-strategy push never entered the display lifecycle, so
their onClose must not be invoked.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs
* feat(notification-badge): use static typo for size=large label (#1521)
Use $font-size.t1-static and $line-height.t1-static so the label is unaffected
by font scaling, and remove the now-unnecessary minAspectRatio spec.
Refs DES-1700
* chore(deps): update anthropics/claude-code-action digest to e58dfa5 (#1498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore(deps): update actions/github-script action to v9 (#1470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 정현수 <hyeonsu.github@gmail.com>
* release: version packages (#1483)
* Version Packages
* Update public rootage version
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(docs): expand ComponentGrid in llms.txt output (#1509)
* feat(docs): expand ComponentGrid in llms.txt output
Add a rule that transforms <ComponentGrid /> into a categorized
markdown list of components so /llms/docs/components.txt exposes
the full component index instead of a bare MDX tag.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(docs): address ComponentGrid rule review feedback
- Treat deprecated: false / "no" / empty as not deprecated so valid
components are no longer filtered out by the string-coerced parser.
- Switch tests to fixture-based equality via readFixture /
normalizeForAssert to match the project's _llms test convention,
and add unit coverage for isDeprecatedValue.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(docs): use getLLMMarkdownUrl helper in ComponentGrid rule
Replace hardcoded /llms/docs/components/{slug}.txt template with the
existing getLLMMarkdownUrl helper so URL construction stays centralized.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(docs): use gray-matter in ComponentGrid rule
Replace the ad-hoc regex parser and string-based isDeprecatedValue
helper with gray-matter, consistent with generate-docs-index. Boolean
frontmatter values are now parsed as real booleans.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(docs): warn when ComponentGrid components directory is missing
Silently returning null masked the original symptom this PR fixes —
the <ComponentGrid /> MDX tag leaking into llms.txt output. Log the
attempted candidate paths so CI/builds can surface the failure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(docs): warn when ComponentGrid transform falls back
Mirror the resolveComponentsDir warning: surfacing the fallback so a
failing getEntries/buildMarkdown can be caught in CI instead of
silently leaving the <ComponentGrid /> tag in llms.txt output.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(deps): update dependency uuid to v14 [security] (#1511)
* fix(deps): update dependency uuid to v14 [security]
* chore(mcp): add uuid security update changeset
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 정현수 <hyeonsu.github@gmail.com>
* chore(mcp): use caret range for zod (#1529)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(docs): upgrade fumadocs to 16.8.2 (#1530)
* chore: bump fumadocs
* refactor(docs): streamline fumadocs source configuration
- Replace getTransformed*PageTree async walker with a sync LoaderPlugin
that reads frontmatter via this.storage.read(filePath); removes the
rootage coupling from the sidebar-badge path
- Drop per-source *Options wrappers in layout.config.tsx; each layout
now composes baseOptions + tree inline
- Align zod to ^4.3.6 to match fumadocs-mdx; loader() now infers the
extended pageData schema correctly, removing the FumadocsSource<TSrc>
narrowing workaround
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(deps): remove 11 unused root devDependencies (#1531)
Removed from root package.json (verified with isolated per-workspace
typecheck + build comparison; results identical across 67 workspaces):
- 7 @changesets/* subpackages (assemble-release-plan, config,
get-dependents-graph, get-github-info, pre, read, types) -
transitively provided by @changesets/cli
- @manypkg/get-packages - transitively provided by @changesets/cli
- @seed-design/qvism-cli - already declared by packages/css
- semver - already declared by packages/cli and packages/codemod
- @types/semver - redundant since semver v7.7+ ships built-in types
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(top-navigation-text-button): add maxWidth to preserve main slot space (#1536)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: pin npmjs.org registry and bump minimumReleaseAge to 3 days (#1541)
- Add .npmrc with registry=https://registry.npmjs.org/ so the internal
proxy URL does not leak into bun.lock, and external contributors can
run `bun i` without extra setup.
- bunfig.toml [install].registry has no effect because bun gives user
~/.npmrc precedence over the project bunfig.toml; project .npmrc wins.
- Bump minimumReleaseAge from 1 day to 3 days to match the registry
proxy policy (only packages older than 3 days are installable).
* feat(top-navigation): rename theme to ios/android and add gradient/layer support (#1542)
* refactor(top-navigation-text-button): scope maxWidth to theme=ios
Move `maxWidth: 96px` from base to the `theme=ios` definition only, and
introduce an empty `theme=android` definition so Android can opt out of
the iOS-style ellipsis cap.
* feat(top-navigation): rename theme=cupertino to theme=ios and add gradient/layer support
- Rename `theme=cupertino` to `theme=ios` to match platform terminology
- Add `gradient` and `bleedBottom` properties to the root slot
- Add `tone=layer` variant so consumers can swap to `$color.bg.layer-basement`
- Add `gradient` variant (only effective when `tone=transparent`); split
`tone=transparent` into `gradient=false` (existing transparent fill) and
`gradient=true` (new linear-gradient with bottom bleed)
- Update qvism stackflow `app-bar`/`app-screen` consumers for the renamed vars
* chore(skills): add snapshot-release skill
Add project skill that orchestrates the `/snapshot` snapshot-release flow
on PR comments end-to-end:
- Discovers the PR for the current branch, any existing `/snapshot` comment, and the local git state in one Bash call.
- If no `/snapshot` exists, asks the user before posting; warns first when there are uncommitted changes or unpushed commits, since the CI builds from the PR's remote HEAD and would silently omit local-only work.
- Finds the matching Continuous Releases workflow run, waits for it to finish, and reports the resulting `📦 Snapshot Release` tarball URLs.
- Bundles every `gh` invocation into one of two Bash calls (discover + orchestrate); re-entrant when the orchestrator dies mid-flight.
* feat(skills): offer "push then post" option in snapshot-release
When the working tree is clean but the local branch has unpushed commits,
add a `Push, then post /snapshot` option to the trigger prompt so the user
can land their work and trigger the snapshot in one step. The orchestrator
honors a new `PUSH_FIRST` flag and runs `git push` before `gh pr comment`.
The push option is intentionally **not** offered when there are uncommitted
changes — `git push` cannot carry those, and offering it would just shift
the source of confusion ("I pushed, why aren't my changes in the snapshot?").
* fix(skills): respect wait-only intent in snapshot-release
When the user's request is clearly wait-only ("snapshot 기다려줘",
"did the snapshot finish?", etc.) and no `/snapshot` comment exists,
don't pivot into the trigger prompt. Tell them no comment was found
and stop, so the skill matches the user's stated intent instead of
silently shifting to a different workflow.
Trigger or ambiguous phrasings still fall through to the AskUserQuestion.
* chore: upgrade TypeScript to 6.0
* chore(figma): sync entities from Figma (#1493)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci(figma-sync): skip claude rerun on matching output and stage all src/ changes
* fix(figma): make componentPropertyDefinitions optional in ComponentMetadata
* Revert "fix(figma): make componentPropertyDefinitions optional in ComponentMetadata"
This reverts commit af722da.
* fix(figma): always emit componentPropertyDefinitions field in extracted entities
* docs: add changeset
* chore(rootage): add divider=false
* perf(stackflow): replace CSS animation with WAAPI for AppScreen transitions (#1444)
* perf(stackflow): replace CSS animation system with WAAPI for all AppScreen transitions
Migrate all AppScreen transition animations (push, pop, swipe back) from
CSS recipe-based animations to Web Animations API (WAAPI). This eliminates
the CSS-to-JS handoff that caused flicker, double-animation, and timing bugs
during swipe-back gestures.
Key changes:
- Remove all transition animation selectors from app-screen.ts and app-bar.ts recipes
- Add transition-animation.ts with WAAPI implementations for iOS/Android/fadeIn styles
- Rewrite useGlobalInteraction to detect transitionState changes and trigger WAAPI
- Add rAF lock pattern and passive-ready touchmove in useSwipeBack
- Manage element positions via explicit inline styles instead of CSS variables
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(stackflow): simplify transition-animation.ts structure
Extract shared helpers to reduce duplication without changing behavior:
- Unify iosAnimatePush/Pop into parameterized iosAnimate
- Unify animateSwipeComplete/Cancel into parameterized animateSwipe
- Extract collectAnimations helper for repeated filter+waitAll pattern
- Fix file header comment to match actual fill:"forwards" strategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(stackflow): make idle/exit positions transition-style-aware
setIdlePositions and setPostExitPositions now accept TransitionStyle
parameter. iOS-specific behind layer offset (-30%) and title/icon
hiding only apply to slideFromRightIOS. Android and fadeIn transitions
no longer incorrectly offset the behind activity.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(stackflow): split transition-animation into dom.ts + animation.ts
- Add data-part attributes to AppBarMain, AppBarIconButton, AppBarSlot
to replace fragile className-based selectors
- Split transition-animation.ts into:
- dom.ts: DOM discovery (findTransitionTargets) and inline style management
- animation.ts: WAAPI animation functions
- Unify SwipeEndpoints with IOS_ONSCREEN/IOS_OFFSCREEN constants
- Remove transition-animation.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(stackflow): add push flash prevention for Android and fadeIn transitions
Set start positions as inline styles before animation begins for
fadeFromBottomAndroid (opacity:0 + translateY:8vh) and fadeIn (opacity:0),
matching the existing pattern in iOS push.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(stackflow): read transitionStyle from DOM instead of stale React state
useTopActivity().transitionStyle updates one render cycle late because it
uses useState + useLayoutEffect. During push, the captured value was the
previous activity's style, so fadeFromBottomAndroid/fadeIn always fell back
to slideFromRightIOS.
Add readTransitionStyle() that reads data-transition-style directly from
the top activity DOM element at animation time, bypassing the stale state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(stackflow): hide behind appBar after Android/fadeIn push
For fadeFromBottomAndroid and fadeIn transitions, the behind activity
stays in place (no parallax offset). After push completes, its appBar
was visible behind the top activity's appBar. Hide it with opacity:0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(stackflow): clearAllStyles as foundation for position management
Root cause: clearAllStyles didn't clear transform/opacity on appBar roots,
leaving stale inline styles from pre-animation setup (e.g. Android push
sets transform:translate3d(0,8vh,0) which was never cleaned up).
Pattern change: setIdlePositions and setPostExitPositions now call
clearAllStyles first (clean slate), then set only non-default positions.
No more selective clearing that misses elements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(stackflow): restore behind appBar before Android/fadeIn pop starts
Behind appBar is hidden (opacity:0) during idle to prevent bleed-through.
Pop must restore it before animation starts so it doesn't flash in at the end.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add changeset for AppScreen WAAPI transition migration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(stackflow): cancel pending push rAF on swipe start
If a push rAF is scheduled but hasn't fired yet when the user starts
swiping, the rAF would execute after swipe begins and overlay push
animations on top of swipe styles.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(stackflow): extract GlobalInteraction constants to constants.ts
* refactor(stackflow): introduce anatomy modules for data-part
* refactor(stackflow): use anatomy constants for WAAPI DOM queries
* chore(qvism-preset): remove unused stackflow animation/pseudo files
* chore: regenerate css artifacts after dev merge
* fix(stackflow): scrub app-bar background via WAAPI during swipe back
The top AppBar background (::before) was not moving with the swipe
gesture because nothing updated the pseudo-element's transform while
the finger dragged. Add a WAAPI scrub animation that replaces itself
on every touchmove and keeps the top app-bar background aligned with
the displacement, uncovering the behind AppBar as intended.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(stackflow): scope WAAPI swipe tracking to slideFromRightIOS
fadeFromBottomAndroid and fadeIn activities should not follow the
finger during swipe back. Cache the top activity's transition style
at gesture start and only apply displacement/complete animations for
slideFromRightIOS; for other styles, rely on stackflow's normal
exit-active lifecycle so the activity's own exit animation runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(stackflow): move AppBar background from ::before to real DOM slot
The previous WAAPI implementation animated the top AppBar background via
`el.animate(frames, { pseudoElement: "::before" })`. That option is only
supported on Chrome 82+ / Safari 16.4+ / Firefox 97+, so on older WebViews
(e.g. Chrome 77) the pseudo-element animation became a no-op and the front
AppBar background stayed put during swipe back, hiding the back AppBar.
Relocate the background into a dedicated `appBarBackground` slot rendered
as a regular `<div>`. Query targets and the `scrubAppBarBackground` swipe
helper now operate on the real element.
- Add `background` slot to `appBar` recipe and anatomy
- Render `<div data-part="appBarBackground" aria-hidden>` inside AppBarRoot
- Move `::before` layout/background/divider styles to the slot
- Expose `topAppBarBackground` / `behindAppBarBackground` on TransitionTargets
- Regenerate css artifacts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(stackflow): harden WAAPI push/pop lifecycle
Three fixes bundled together since they touch the same transition
coordination code path.
1. Pre-pin inline styles before push (`pinIosInlineStyles` helper).
Browsers could paint the freshly mounted top activity at its final
onscreen position for one frame before the compositor picked up
`el.animate()`, so push animations appeared to start mid-way on heavy
activities. Pin the iOS offscreen coordinates (and the Android /
fadeIn equivalents) inline within the same task before invoking
animate, guaranteeing the first paint lands at the start of the
animation curve.
2. Polyfill `Animation.finished` for Chrome <84.
Older WebViews ship WAAPI without the `finished` promise. The old
`waitAll` simply called `a.finished.catch(...)` which threw and left
the transition's cleanup handler unreachable — subsequent pops ran
over stale inline styles and rendered a blank screen. `waitOne` now
falls back to `onfinish` / `oncancel` listeners and races every
animation against a `duration + 100ms` timeout so cleanup always
runs even if the browser never dispatches finish.
3. Defer push animation one rAF for DOM readiness.
Running push sync inside `useLayoutEffect` raced with stackflow's
own subscription update — `[data-activity-is-top]` was not yet
observable on the new top, so `findTransitionTargets` returned nulls
and no enter animation played. Wrap the push path in
`requestAnimationFrame` (guarded by `pendingPushRAFRef`) to let
stackflow finish committing before querying targets. Pop stays sync
since its top activity already lives in the DOM.
Also keep the scrub animation `appBarBgScrubAnimRef` in sync with the
new real background element renamed from the legacy pseudo naming.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(changeset): document WAAPI stabilization fixes
Extend the existing AppScreen WAAPI changeset with the three follow-up
bullets for the jank, older-WebView, and pseudo-element regressions
addressed in this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(stackflow): clean up WAAPI hooks and tighten types (#1540)
* refactor(stackflow): clean up WAAPI hooks and tighten types
- Move prop callback handling out of getSwipeBackEvents factory into
useSwipeBack to fix a rules-of-hooks violation (useCallback / useMemo
/ useCallbackRef were called inside another useCallback)
- Add runningAnimsRef race guard to all finished.then handlers so a
stale handler can't clobber a newer transition's pinned inline styles
(the comment promised this but the guard was missing)
- Add useEffect cleanup to cancel pending RAF / running animations on
unmount so late-firing finished handlers can't run against a torn-down
stack
- Extract stopRunningAnims, stopAppBarBgScrub, cancelPendingPushRAF
helpers to remove repeated cancel + null-out boilerplate
- Introduce frozen IDLE_CONTEXT constant for swipe-back context resets
- Tighten IosPositions title fields from Keyframe to a strict
TitleKeyframe { opacity: string; transform: string } to drop casts
- Drop redundant null guards in pinIosInlineStyles
- Replace findTransitionTargets backwards loop with findIndex
- Mutate swipeBackContextRef.current in place during 60fps moveSwipeBack
to avoid per-frame object allocation
- Drop unused setSwipeBackState, swipeBackContextRef, swipeBackStateRef
from useGlobalInteraction return
* fix(stackflow): always cleanup inline styles after WAAPI transition
The race-condition guard added in d4c7547 was too strict: when stackflow
fires a follow-up transitionState change near the end of a pop (e.g. an
enter-active for the activity that becomes top), the new transition cancels
the running animation, the cancel drops fill:forwards, and the previous
guard returned without running setPostExitPositions / setIdlePositions —
leaving the stale idle inline styles (behind layer at -30%) visible on
screen.
Reorder so cleanup runs unconditionally on finish:
- Always run setPostExitPositions / setIdlePositions / pin + cancelAll.
- Only the ref / per-gesture cache reset is guarded so a newer transition's
state isn't clobbered.
Repro: push → pop quickly. Pop ends with behind layer stuck at translate3d(-30%, 0, 0).
* fix(stackflow): pin swipe-end duration to 350ms to match CSS-era timing
Restore the pre-WAAPI swipe transition feel. The dynamic
calculateSwipeDuration (150-500ms based on remaining distance / velocity)
shortened cancel/complete transitions enough — especially for short
gestures or fast flicks — to feel abrupt. Pin to IOS_DURATION (350ms)
exactly as the CSS-era createPresence configured it.
- Drop calculateSwipeDuration, MIN/MAX_SWIPE_DURATION constants.
- Drop unused velocity parameter from animateSwipeComplete/Cancel.
* fix(stackflow): keep swipe-time inline styles to avoid 1-frame snap
endSwipeBack used to call clearAllStyles(targets) right before starting
the WAAPI cancel/complete animation. On heavy main-thread sessions where
a reflow/paint can interleave between the inline clear and the
animation's first paint, the screen briefly snapped to default
(transform: 0) before the keyframe[0] reapplied the swipe displacement —
felt as a "stutter" mid-gesture.
The clear is redundant: WAAPI keyframe[0] already matches the current
swipe displacement, and onFinish's pin() resets inline state at the end.
Drop the call so the inline values cover the 1-frame gap before the
animation's first paint.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Version Packages (#1526)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(agents): strengthen component guidance (#1520)
* docs(agents): strengthen component guidance from accordion review
* docs(agents): clarify headless hook boundaries
* docs(agents): refresh generic component guidance
* docs(agents): clarify affix slot guidance
* docs(agents): address guidance review comments
* docs(create-component): clarify component review guidance
* docs(stackflow): document modal-on-modal stacking for Bottom Sheet (#1547)
Add a "Stacking Another Modal Overlay on Top" section under Keeping Bottom
Sheet Mounted in the Bottom Sheet Stackflow guide. It explains when to use
`modal` (always true) instead of `modal={isActive}` so the backdrop layering
does not break when another modal Activity is pushed on top, with a tradeoff
callout for the general AppScreen case.
Add example activities under examples/stackflow-spa for live reference:
- ActivityNestedBottomSheet: embedded into the new docs section via
StackflowExample at /nested-bottom-sheet
- ActivityBottomSheetWithAlertDialogStep: kept as an internal Step-pattern
reference (not embedded in docs) to support 1:1 follow-ups when the
Step-with-Step nested overlay case is asked
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(stackflow): tighten peerDependencies `@seed-design/css` to WAAPI compatible range (#1551)
* fix(stackflow): tighten peerDependencies['@seed-design/css'] to WAAPI-compatible range
* Update many-mails-sleep.md
* Version Packages (#1553)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore(deps): update anthropics/claude-code-action digest to 9db782c (#1534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore(deps): update dependency chromatic to v16 (#1523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore: regenerate artifacts after dev merge
- bun install: refresh lockfile
- bun generate:all: regenerate css min bundles + lynx-css vars (top-navigation icon/text button vars added from #1542)
* fix(changeset): drop private @seed-design/qvism-preset from lynx-checkbox-radio-group
The changeset bundled a private package (@seed-design/qvism-preset, marked
"private": true in its package.json) with two public packages
(@seed-design/lynx-react, @seed-design/lynx-css). changesets refuses such
mixed changesets and the Version Packages CI on lynx kept failing with:
Found mixed changeset lynx-checkbox-radio-group
Found ignored packages: @seed-design/qvism-preset
Found not ignored packages: @seed-design/lynx-react @seed-design/lynx-css
Mixed changesets that contain both ignored and not ignored packages are not allowed
Removing the private package's bump line fixes the error. qvism-preset
is private so it never gets a release tag/version anyway; the recipe-add
context stays documented in the body text.
* fix(docs): add leading './' to seed-design path mapping
TypeScript 6.0 (merged from dev via c5012d1) tightens 'paths' validation
and refuses non-relative entries when 'baseUrl' is not set, breaking the
docs production build with:
Type error: Non-relative paths are not allowed when 'baseUrl' is not set.
Did you forget a leading './'?
The lynx branch had 'registry/react/*' (no leading dot-slash), which used
to work on the older TS version. Match dev's existing convention of
prefixing the path with './'.
---------
Co-authored-by: Joo Chanhwi <56245920+te6-in@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 유세은 <101736358+SeieunYoo@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR contains the following updates:
905d4eb→e58dfa5Warning
Some dependencies could not be looked up. Check the Dependency Dashboard for more information.
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.