diff --git a/README.md b/README.md index 355959a..f98eed4 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ Accessible React Native star rating, drag rating, and semantic feedback scale fo Fractional values, RTL and vertical layouts, keyboard and screen-reader support, a FlatList-friendly display path, and zero runtime dependencies. +

+ Five outlined stars filling with gold one by one +

+ ## Why this rating component? - **One accessible control.** Native assistive technologies get an adjustable control; the Web gets a real ARIA slider with keyboard input. @@ -22,14 +26,6 @@ Fractional values, RTL and vertical layouts, keyboard and screen-reader support, npm install react-native-rating ``` -```sh -yarn add react-native-rating -``` - -```sh -bun add react-native-rating -``` - The package requires React 19.1.1 or newer and React Native 0.82 or newer. It uses only React and React Native core APIs, so Expo projects need no native module setup. React Native Web support assumes the application already has its normal Web target configured. ## Quick start @@ -378,22 +374,6 @@ The components own their root responder, keyboard, accessibility, focusability, For large lists, prefer `RatingDisplay`, keep `items` arrays and `renderItem` callbacks stable, and give the list a stable `keyExtractor`. -## Feature comparison - -This is a source-level comparison of the versions audited on 2026-07-28, not a popularity claim. “Partial” means the package covers some of the behavior but not the complete row. - -| Capability | `react-native-rating` | [`react-native-star-rating-widget` 1.11.0](https://github.com/bviebahn/react-native-star-rating-widget/tree/c8906b677061ca7ac252c191419495e8cdefa381) | [`@kolking/react-native-rating` 1.4.1](https://github.com/kolking/react-native-rating/tree/19382752e7a47cd5370d0e5206fc107ad52be3e2) | [`react-native-ratings` 8.1.0](https://github.com/Monte9/react-native-ratings/tree/0a8f16b4380626556b636e8883b06c0db9e885ec) | -| --- | --- | --- | --- | --- | -| No required third-party runtime package | Yes | No; requires `react-native-svg` | Yes | No; depends on `lodash` | -| Deliberate, cross-axis-aware drag ownership | Yes | No; start/move capture is unconditional | No; starts immediately and refuses termination | No; starts immediately | -| Fraction selection and visual fill share one exact model | Yes, `0.01`–`1` | Fixed full/half/quarter | Partial; decimal display, whole-item input | Partial; an [open issue](https://github.com/Monte9/react-native-ratings/issues/155) reports jump/UI divergence | -| Dedicated allocation-light static component | Yes | Yes | No; uses `disabled` on the interactive component | No; `readonly` stays in the swipe component | -| Native adjustable semantics plus Web ARIA slider keys | Yes | Partial; native actions, no direct Web key handler in audited source | No control semantics in audited source | No; [accessibility remains open](https://github.com/Monte9/react-native-ratings/issues/172) | -| Explicit LTR/RTL override and vertical orientation | Yes | Partial; automatic RTL, horizontal | Partial; automatic RTL, horizontal | No; [RTL issue open](https://github.com/Monte9/react-native-ratings/issues/81) | -| Generic NPS/Likert/emoji scale with `null`, zero, negatives, and strings | Yes | No | No | No; [bidirectional scale request open](https://github.com/Monte9/react-native-ratings/issues/189) | - -See the dated [competitive analysis](./docs/COMPETITIVE_ANALYSIS.md) for the search method, source audit, issue evidence, and positioning decisions. See the [architecture and release plan](./docs/ARCHITECTURE.md) for interaction, performance, accessibility, and QA invariants. - ## Compatibility | Dependency or platform | Supported baseline | diff --git a/assets/star-rating-readme.svg b/assets/star-rating-readme.svg new file mode 100644 index 0000000..0da13a0 --- /dev/null +++ b/assets/star-rating-readme.svg @@ -0,0 +1,35 @@ + + + React Native Rating star animation + Five white stars outlined in gray fill with gold one by one, pulse on completion, then reset for a loop. + + + + + + + + + + + + + + + + + + + diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md deleted file mode 100644 index 97635fe..0000000 --- a/docs/ARCHITECTURE.md +++ /dev/null @@ -1,480 +0,0 @@ -# Version 4 architecture and integrated release plan - -Status date: **2026-07-28** - -## Release contract - -The next version ships as one integrated pull request and one release. There are no intermediate packages, temporary feature flags, parallel gesture engines, or compatibility components to remove later. - -The release is complete only when all of these move together: - -- numeric rating correctness; -- tap/drag/keyboard/accessibility interaction; -- exact static display; -- semantic scales; -- custom rendering; -- RTL and vertical layout; -- reduced-motion animation and list performance; -- examples, migration guidance, package compatibility, and release evidence. - -“Latest” means the latest validated combination, not blindly accepting every new dependency. A dependency update that prevents the main check or React Native matrix from running is not release-ready. - -## Architectural decisions - -### Compose three jobs instead of one mode-heavy component - -The public API has three focused components: - -| Component | Job | Empty value | Interaction allocation | -| --- | --- | --- | --- | -| `Rating` | Select a numeric aggregate across repeated visual items | `0` | Only when not `readOnly` | -| `RatingDisplay` | Render an exact read-only aggregate | `0` | None | -| `RatingScale` | Select an ordered semantic choice such as NPS/Likert/emoji | `null` | Only when not `readOnly` | - -This prevents incompatible semantics from accumulating as boolean props on one component. In particular, a star rating can safely reserve zero for “unrated,” while NPS and bipolar scales must preserve zero and negative values. - -### One root interaction surface - -The visual items do not each own a Pressable, PanResponder, listener, or accessibility stop. One measured root track maps position to a logical tick and one accessible root describes the complete control. - -This improves: - -- fractional selection across item boundaries; -- drag continuity through gaps; -- responder negotiation with a parent ScrollView; -- callback deduplication; -- keyboard and assistive-technology parity; -- allocation behavior in repeated controls. - -### Core React Native is sufficient - -The control uses the Gesture Responder System rather than adding Gesture Handler or Reanimated. A rating input needs axis-intent negotiation and simple position mapping, not a general gesture graph. Avoiding a gesture dependency also preserves the zero-runtime-dependency installation. - -The boundary is intentional: if future requirements add simultaneous gestures, velocity/decay, multi-touch, or complex cross-component coordination, revisit the decision rather than stretching the current responder. - -## Module map - -```mermaid -flowchart TD - Index["src/index.ts
public exports"] --> Rating["Rating
numeric state facade"] - Index --> Display["RatingDisplay
static facade"] - Index --> Scale["RatingScale<Value>
semantic state facade"] - Index --> Types["types.ts
public contracts"] - - Rating --> Root["InteractiveRoot
native + Web semantics"] - Rating --> Interaction["useRatingInteraction
state machine"] - Rating --> Pulse["useSelectionPulse
completion feedback"] - Rating --> Model["model.ts
ticks, normalization, layout mapping"] - Rating --> Track["rating-track.tsx
visuals and measured surface"] - - Scale --> Root - Scale --> Interaction - Scale --> Pulse - Scale --> Model - Scale --> Track - - Display --> Model - Display --> Track - - Pulse --> Motion["reduced-motion.ts
shared external store"] -``` - -The public facades own controlled/uncontrolled state. Internal modules own one kind of policy each; consumers never import them directly. - -## Numeric value model - -Floating-point values are converted to integer ticks before interaction: - -```text -step = clamp(finite step, 0.01, 1) -ticksPerItem = ceil(1 / step) -maxTick = maxItems × ticksPerItem -``` - -Each tick is converted back by splitting it into full items plus a partial tick. The end of each item is always exactly the next integer item value. For a step that does not divide one evenly, such as `0.3`, the selectable sequence inside an item is `0.3`, `0.6`, `0.9`, then `1`, not `1.2`. - -Values are exposed on a bounded six-decimal lattice. Ceil boundaries first check whether the nearest integer multiple lands on that same exposed lattice, then fall back to the raw quotient. This stabilizes decimal boundaries such as `0.01`/`0.07`, arbitrary-precision inputs, and rational steps such as `1 / 7` without creating duplicate ticks or overshooting a value the component itself emitted. - -Invariants: - -1. `max` is a finite integer clamped to `1…100`. -2. Interactive `step` is finite and clamped to `0.01…1`. -3. `0` is the numeric unrated sentinel. -4. Every positive value normalizes to a selectable tick. -5. `min` rounds upward to the next selectable positive tick; it never permits a value lower than requested. -6. Controlled, uncontrolled, pointer, keyboard, accessibility, callback, and visual values all pass through the same model. -7. `NaN`, infinities, negative sizes, negative gaps, and out-of-range values are normalized before a native style is constructed. Item size and gap are defensively capped at `1024` so finite-but-extreme input cannot create an unsafe track extent. -8. `RatingDisplay` is different by design: without `step`, it clamps and draws the exact aggregate rather than snapping to an input tick. - -The native accessibility range uses integer ticks (`now` and `max`) to avoid platform precision conversion problems. The spoken text and Web ARIA value use the consumer-facing decimal. - -## Semantic scale model - -`RatingScale` accepts finite numbers or strings: - -```ts -interface RatingScaleItem { - value: Value; - label: string; - content?: ReactNode; -} -``` - -Invariants: - -- `null` alone means empty; `0`, negative values, and empty strings remain semantic values. -- Duplicate values are removed while preserving the first item; numeric `-0` and `0` are one semantic choice. -- Invalid values and blank labels are ignored and the list is capped at 100 items. -- Interaction uses a one-based internal tick, while callbacks return the original semantic value. -- `selectionMode="single"` highlights one choice; `selectionMode="cumulative"` highlights every semantic item through it. -- `reversed` maps the ordered source items onto the opposite logical ticks. It changes semantic progression without reversing coordinate math and remains independent of LTR/RTL layout. -- `itemExtent` gives each choice a primary-axis length independent of visual `size`. It normalizes to at least `size`, is capped at `1024`, and is exposed to custom renderers; the default horizontal text renderer uses it as available label width. -- Missing, `null`, or boolean `content` falls back to the required human-readable label. - -Keeping the original value in callbacks preserves TypeScript inference for literal string unions and avoids a separate index-to-value lookup in the consumer. - -## Position and layout model - -The track is measured on layout. A deterministic primary-axis extent is used until measurement is available. `itemPrimaryExtent` is `size` for numeric ratings and `itemExtent` for semantic scales: - -```text -extent = itemCount × itemPrimaryExtent + (itemCount - 1) × gap -``` - -At responder grant: - -```text -origin = pagePrimary - locationPrimary -``` - -During movement: - -```text -localPrimary = pagePrimary - origin -``` - -Move-time `locationX`/`locationY` is never trusted. This follows the failure documented in [react-native#15290](https://github.com/react/react-native/issues/15290) and [react-native-web#693](https://github.com/necolas/react-native-web/issues/693). - -Mapping rules: - -- positions outside the track clamp to its logical ends; -- horizontal RTL mirrors logical position across the measured extent; -- vertical logical progression runs bottom-to-top and is independent of locale direction; -- the midpoint of a visual gap decides which adjacent selectable edge wins; -- fractional fill originates left in LTR, right in RTL, and bottom vertically; -- `RatingScale.reversed` maps a semantic item to the opposite logical tick; it does not add a second coordinate reversal. - -The root style applies structural direction last so a forwarded style cannot silently desynchronize visuals from hit testing. - -## Interaction state machine - -```mermaid -stateDiagram-v2 - [*] --> idle - idle --> pressed: one pointer granted - pressed --> idle: release / commit tap - pressed --> cancelled: tap moves beyond slop - pressed --> cancelled: cross-axis intent dominates - pressed --> dragging: primary-axis intent dominates - pressed --> dragging: release proves primary-axis intent - pressed --> idle: responder terminated before acceptance - dragging --> dragging: distinct tick / emit onChange - dragging --> idle: release / emit final + completion - dragging --> cancelled: termination, disable, structure change, or multi-touch - cancelled --> idle: release or reset -``` - -Constants are implementation policy, not public API: - -- movement slop: 7 logical pixels; -- axis dominance ratio: 1.25. - -Responder behavior: - -1. A single pointer can enter `pressed`; multi-touch is rejected. -2. `pressed` is a visual preview only. It fires no lifecycle callback until a tap releases or a deliberate drag is accepted. -3. In `tap` mode, movement beyond slop cancels the pending tap. -4. In `tap-and-drag`, dominant cross-axis movement cancels and permits the parent scroll responder to take over. -5. Release coordinates are checked against the same slop and axis rules. A tap cannot commit merely because no move event arrived; a primary-dominant release in drag mode can still accept and commit the drag. -6. Before drag acceptance, termination requests return true. Once a primary-axis drag is accepted, termination requests return false so the selected value remains stable. -7. A second pointer cancels the gesture. If a drag was already accepted, cancellation ends it exactly once. -8. Disabling the control or changing value structure such as max/step/orientation/direction during an accepted drag cancels it. The final callback decodes through the grant-time model. -9. Drag movement emits each tick at most once, regardless of controlled parent latency. -10. A post-acceptance termination reports `onChangeEnd(..., { cancelled: true })`. -11. No animation runs while dragging. - -On the Web, the horizontal track sets `touchAction: "pan-y"` and a vertical track sets `touchAction: "pan-x"` when drag is enabled. That preserves the browser's cross-axis scrolling contract. - -## Controlled state and callbacks - -The prop is always the source of truth in controlled mode. During an active gesture, a local draft tick gives immediate visual response while the parent renders. The gesture also retains its last emitted tick so a slow controlled parent does not receive repeated identical changes. - -Callback contract: - -| Callback | Frequency | Intended use | -| --- | --- | --- | -| `onInteractionStart(value, { source })` | Once per accepted pointer, keyboard, or accessibility interaction | Interaction analytics and lightweight UI state | -| `onChange(value)` | Once per distinct selected tick/value; can be many during drag | Local controlled state | -| `onChangeEnd(value, { source, cancelled })` | Once per accepted interaction | Persistence, validation, analytics completion | - -Keyboard and accessibility operations still have a start/end lifecycle when already at a boundary, but emit no `onChange` if the value did not change. - -`allowClear` is deliberately narrow: - -- a true same-value tap clears; -- decrement at the minimum clears; -- Web Home preserves an existing empty sentinel; from a nonempty value it selects zero only with `allowClear`, otherwise it selects `min`; -- no nonempty interaction returns to the empty sentinel unless `allowClear` is enabled. - -An incidental drag back through the starting tick does not become a same-value-tap clear. - -## Accessibility architecture - -### Native - -- One root is `accessible` with `accessibilityRole="adjustable"`. -- Its numeric accessibility value contains integer tick `min`, `max`, and `now`, plus localized text. -- Increment/decrement actions use the same tick functions as keyboard input. -- Visual descendants are hidden from the accessibility tree, preventing five or more repetitive focus stops. -- `disabled` is reflected in accessibility state and removes actions. -- Static components use one image/content semantic with accessible value text. - -### Web - -React Native Web maps many accessibility props, but its current handling does not provide the complete numeric slider contract from the native `accessibilityValue` object. The root therefore receives direct: - -- `role="slider"`; -- `aria-label`, `aria-disabled`, `aria-orientation`; -- `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext`; -- `tabIndex={0}` when enabled and `-1` when disabled; -- a two-color visible focus indicator: a blue outline plus a white separation ring. `focusStyle` composes after the default while focused. - -Keys follow the [WAI-ARIA slider pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider/): - -| Key | Result | -| ----------------------- | ------------------------------------------ | -| Right Arrow / Up Arrow | Increment one tick | -| Left Arrow / Down Arrow | Decrement one tick | -| Home | Minimum, or empty when clearing is allowed | -| End | Maximum | - -Handled unmodified keys prevent default and stop propagation. Unrelated keys and Arrow/Home/End combinations with Alt, Control, or Meta are left to the application. - -### Touch target and visual access - -The interactive cross axis is at least 44pt on iOS and 48dp elsewhere, following [Apple](https://developer.apple.com/design/human-interface-guidelines/accessibility) and [Android](https://developer.android.com/guide/topics/ui/accessibility/views/apps-views?hl=en) guidance. The primary-axis width remains the actual item width, avoiding overlapping hit targets and misleading gaps. - -The default selected star is filled (`★`) while the unselected star is outlined (`☆`), so state is not encoded by color alone. Default colors are chosen for strong contrast on a light surface, and the default two-color focus treatment is designed to remain distinguishable on both light and dark surroundings. Applications remain responsible for checking custom colors, components, and `focusStyle` in their actual backgrounds and themes against [WCAG non-text contrast](https://www.w3.org/TR/WCAG22/#non-text-contrast) and [WCAG Focus Appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html). - -Static visuals expose one image/content semantic by default. `RatingDisplay decorative` and a read-only `RatingScale decorative` hide a duplicate visual subtree when adjacent content already communicates the same value; `decorative` is ignored for an interactive scale. - -## Animation and reduced motion - -Animation communicates completion, not continuous value: - -- one `Animated.Value` per interactive control; -- a 150ms scale from `0.92` to `1`; -- transform-only native-driver animation on iOS and Android; -- `useNativeDriver: false` on the Web; -- `isInteraction: false`, so list rendering is not held behind an interaction handle; -- no bounce, spring, or delayed timer after every drag tick. - -Reduced motion is a module-level external store: - -- the first animated control creates one `AccessibilityInfo` subscription; -- all controls share its snapshot; -- the last subscriber removes the native listener; -- an async preference response is generation-guarded so a stale request cannot update an unmounted store; -- initial and server snapshots conservatively reduce motion; -- static and `animated={false}` controls never subscribe. - -## Rendering and allocation strategy - -- `RatingDisplay` renders only normalized visuals and one accessible root. -- Read-only `RatingScale` uses the same static principle. -- Responder callbacks are stable and read the latest configuration/callback refs. -- Numeric and semantic item components are memoized; stable untouched semantic items have a render-count regression test. -- Visual children use registered React Native styles for `pointerEvents="none"`/a box-only track so custom content cannot split responder ownership without leaking native-only pointer-event values into React Native Web inline CSS. -- Only the selected completion item receives the shared animated transform. -- Invalid custom values are normalized at the facade, not repeatedly inside each visual item. -- Consumer `style`, focus/blur handlers, test IDs, and refs compose at the root. Owned responder, keyboard, focusability, pointer-routing, role/tab-order, ARIA, and native accessibility props are excluded from the public root type and stripped again at runtime for JavaScript or broad object spreads. - -No performance claim should be based on allocation reasoning alone. Automated gates cover static allocation boundaries, memoized-item render counts, packed contents, and supported consumer builds. Release-mode list profiling remains a publication check because timing thresholds inside shared CI are not reliable device benchmarks. - -## Integrated implementation plan - -The phases are review boundaries inside one pull request, not separate releases. - -### Phase 1: evidence and public contract - -- Freeze the dated competitor/source/issue evidence. -- Define `Rating`, `RatingDisplay`, and generic `RatingScale` responsibilities. -- Define callback lifecycle, value sentinels, layout terms, and normalization bounds before visual implementation. - -Gate: every public prop has one semantic meaning and no component relies on an ambiguous zero/null convention. - -### Phase 2: pure model - -- Implement numeric tick conversion and display normalization. -- Implement track/gap/direction/orientation position mapping. -- Implement semantic scale mapping and discrete increment/decrement/Home functions. - -Gate: boundary-focused model tests run without a renderer for non-divisor steps, invalid numbers, RTL, vertical, gaps, min, and clear; facade tests cover semantic reversal and duplicate scale values. - -### Phase 3: static rendering - -- Implement common track and numeric/scale visuals. -- Implement exact `RatingDisplay`. -- Route `readOnly` facades to allocation-light display components. -- Add typed custom render slots and accessible static text. - -Gate: static trees contain no responder handlers, Animated values, or reduced-motion subscriptions and render exact values such as `4.37`. - -### Phase 4: interaction engine - -- Implement the `idle → pressed → dragging/cancelled` state machine. -- Use grant-time origin plus page coordinates. -- Add axis slop/dominance, termination rules, multi-touch cancellation, local draft, and per-gesture deduplication. -- Connect tap, drag, min, clear, and semantic mapping to the pure model. - -Gate: responder tests prove cross-axis yield, primary-axis retention, callback order, cancellation, outside bounds, gaps, slow controlled parents, and dynamic callback/config freshness. - -### Phase 5: accessibility, Web, and motion - -- Add the native adjustable root and direct Web slider adapter. -- Add keyboard/Home/End behavior, focus composition, and disabled semantics. -- Add target-size policy, visual subtree hiding, and localized value text. -- Add one completion pulse and the shared reduced-motion store. - -Gate: native accessibility-action tests, Web ARIA/key tests, focus tests, reduced-motion subscription tests, and a manual VoiceOver/TalkBack/keyboard pass. - -### Phase 6: consumer proof - -- Update the type-consumer fixture for every component and generic inference. -- Provide examples for controlled/uncontrolled, drag, exact FlatList display, custom rendering, RTL, vertical, NPS, Likert, emoji, and negative scales. -- Install the packed tarball into a clean Expo example, type-check it, export Web, and server-render representative React Native Web trees. -- Rewrite README/package discovery text around verified use cases. - -Gate: examples type-check, package imports resolve in ESM/CommonJS, the packed package completes an Expo Web export, and the React Native Web DOM smoke rejects render diagnostics, missing slider/decorative semantics, or invalid pointer-event output. - -### Phase 7: release evidence - -- Run formatting, lint, TypeScript, unit/coverage, build, `publint`, Are the Types Wrong, tooling/security checks, and supported React Native matrices. -- Profile a large static list and a smaller interactive list in release mode before publication; keep deterministic render-count regressions in CI. -- Review the packed tarball, exports, peer ranges, bundle/package size, and changelog. -- Link issue dispositions and this evidence in the draft pull request. - -Gate: one green integrated pull request. Versioning remains with Release Please; no manual version-only intermediate commit. - -## QA matrix - -### Pure model - -- max/min/step normalization, including `NaN`, infinities, negative values, and item cap; -- whole, half, quarter, tenth, minimum `0.01`, and non-divisor steps; -- exact display versus intentionally snapped display; -- LTR/RTL, horizontal/vertical, gap midpoints, outside bounds; -- scale null/zero/negative/string values, duplicates, invalid values, `reversed`, and selection modes. - -### Interaction - -- tap commit, same-value clear, tap movement cancellation; -- primary drag threshold and cross-axis ScrollView yield; -- grant/move coordinate conversion using changing event targets; -- distinct-tick emissions and slow controlled parent; -- responder termination before and after drag acceptance; -- multi-touch rejection/cancellation; -- dynamic callbacks and dynamic max/min/step/orientation/direction; -- uncontrolled state re-normalization when configuration changes. - -### Accessibility and Web - -- one adjustable native node; visual descendants hidden; -- native increment/decrement and boundary no-op; -- integer native range plus localized text; -- Web slider role, direct ARIA range/value/orientation, disabled tab order; -- Arrow/Home/End behavior, preventDefault, and unrelated key pass-through; -- focus/blur callback composition and visible focus style; -- static image/content semantics; -- reduced-motion initial, changed, rejected-promise, shared-subscription, and cleanup paths. - -### Rendering and performance - -- exact partial masks and fill origin for LTR/RTL/bottom-to-top vertical; -- custom render props for numeric and semantic items; -- target dimensions without primary-axis inflation; -- no interactive allocations on static paths; -- one responder root and one pulse value per interactive control; -- stable semantic-item render counts in CI plus a release-mode list profile before publication; -- no animation during drag and native-driver completion on native. - -### Package and compatibility - -- minimum and current supported React/React Native pairs; -- iOS, Android, and React Native Web; -- Expo managed Web export plus packed-package React Native Web server-render smoke; -- ESM, CommonJS, TypeScript declarations, and source export condition; -- packed contents, `sideEffects: false`, runtime dependency count, and peer ranges; -- consumer fixture generic inference for literal-string scales. - -## Release gates - -Automated baseline: - -```sh -bun run check -``` - -That command must include formatting, lint, TypeScript, coverage, package build/validation, and repository tooling checks. The release PR must also show green React Native compatibility jobs. - -Manual release checklist: - -- VoiceOver: focus, value announcement, increment/decrement, disabled, static; -- TalkBack: the same paths; -- Web: mouse, touch, keyboard, two-color focus indicator/custom `focusStyle`, browser zoom, high contrast; -- vertical rating inside horizontal scroll and horizontal rating inside vertical scroll; -- LTR application, RTL application, explicit local direction override; -- reduced motion enabled and disabled; -- FlatList with exact static values and interactive rows; -- dark/light custom backgrounds and localized value text. - -## Explicit non-goals for this release - -- Sparse disabled indexes. `min` solves the verified request without creating discontinuous slider semantics. -- Haptics or sound. These require application policy and platform permissions; callbacks let the consumer add them deliberately. -- A bundled icon/SVG library. The core star and render slots avoid forcing a visual dependency. -- Per-item accessibility stops. The control is one logical slider. -- Velocity, momentum, or spring-based drag. A rating should select deterministically where the user points. -- Multi-touch rating. -- Manual package version edits outside the existing Release Please flow. - -## Definition of done - -The next version is ready when: - -1. Every local issue/PR has a documented disposition. -2. Verified competitor pain has a corresponding invariant and regression test, not merely a marketing bullet. -3. Static and interactive responsibilities remain separable in code and package types. -4. Native, Web, RTL, vertical, controlled, uncontrolled, reduced-motion, and semantic-scale paths pass their matrices. -5. The packed package has zero runtime dependencies and valid ESM/CommonJS/type exports. -6. README examples match the shipped public API and the comparison remains source-linked and dated. -7. The integrated pull request is green and reviewable. -8. Release Please can produce the one intended next version without an intermediate publish. - -## Primary references - -- [React Native Gesture Responder System](https://reactnative.dev/docs/gesture-responder-system) -- [React Native PanResponder](https://reactnative.dev/docs/panresponder) -- [React Native Animated](https://reactnative.dev/docs/animated) -- [React Native performance](https://reactnative.dev/docs/performance) -- [React Native accessibility](https://reactnative.dev/docs/accessibility) -- [React Native AccessibilityInfo](https://reactnative.dev/docs/accessibilityinfo) -- [React Native I18nManager](https://reactnative.dev/docs/i18nmanager) -- [React Native Web interactions](https://necolas.github.io/react-native-web/docs/interactions/) -- [React Native Web accessibility](https://necolas.github.io/react-native-web/docs/accessibility/) -- [WAI-ARIA slider pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider/) -- [Apple accessibility](https://developer.apple.com/design/human-interface-guidelines/accessibility) -- [Android accessible views](https://developer.android.com/guide/topics/ui/accessibility/views/apps-views?hl=en) -- [WCAG 2.2 non-text contrast](https://www.w3.org/TR/WCAG22/#non-text-contrast) -- [WCAG 2.2 Focus Appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html) -- [Dated competitor and issue evidence](./COMPETITIVE_ANALYSIS.md) diff --git a/docs/COMPETITIVE_ANALYSIS.md b/docs/COMPETITIVE_ANALYSIS.md deleted file mode 100644 index 4c6dae4..0000000 --- a/docs/COMPETITIVE_ANALYSIS.md +++ /dev/null @@ -1,280 +0,0 @@ -# Competitive analysis: React Native rating components - -Evidence date: **2026-07-28** - -Search capture: **2026-07-28 04:07 UTC**, using an English-language/global research surface. - -This document records the evidence behind the next version of `react-native-rating`. It separates observed facts from product inferences so the public positioning stays defensible. - -## Scope and method - -The audit combined four evidence sets: - -1. The first-two-page result depth for the query families “react native rating component,” “react native star rating component,” “React Native ratings library,” and “React Native rating input Expo Web accessibility.” -2. Current npm package metadata and the npm downloads API. -3. The published source or current release commit of recurring results. -4. Open and historically relevant GitHub issues, plus this repository's open issues and pull requests. - -The research surface merged some query results and did not expose stable per-query rank or locale metadata. Search order is also volatile and personalized. The package map is therefore a recurring-hit sample across the reviewed first-two-page depth, npm, and GitHub—not a reproducible ranking or a claim that every user will see the same ordering. In-app store-review launchers such as `react-native-rate` were excluded because they solve a different job. - -The download count is the npm `last-week` result for **2026-07-18 through 2026-07-24**. `@rneui/base` is a toolkit-wide count and cannot be attributed to its Rating component alone. - -## Search-hit and package map - -| Package | Audited release/source | npm downloads | What it establishes | -| --- | --- | --: | --- | -| [`react-native-ratings`](https://www.npmjs.com/package/react-native-ratings) | [8.1.0 / `0a8f16b`](https://github.com/Monte9/react-native-ratings/tree/0a8f16b4380626556b636e8883b06c0db9e885ec) | 135,113 | Strong name recognition; tap/swipe modes, built-in image types, review labels | -| [`@rneui/base`](https://www.npmjs.com/package/@rneui/base) | [5.0.0 / `32ae0ed`](https://github.com/react-native-elements/react-native-elements/tree/32ae0eda7c559f55a9d858fc6880ed46f7714c5b) | 68,276 toolkit-wide | Rating bundled into a broad UI system; current wrapper still derives from a swipe-rating design | -| [`react-native-star-rating-widget`](https://www.npmjs.com/package/react-native-star-rating-widget) | [1.11.0 / `c8906b6`](https://github.com/bviebahn/react-native-star-rating-widget/tree/c8906b677061ca7ac252c191419495e8cdefa381) | 16,768 | Modern TypeScript, animation, full/half/quarter input, custom icon, separate display | -| [`react-native-star-rating`](https://www.npmjs.com/package/react-native-star-rating) | [1.1.0](https://github.com/djchie/react-native-star-rating) | 3,690 | An old result that continues to rank; vector-icon-era installation and per-star buttons | -| [`@rn-vui/ratings`](https://www.npmjs.com/package/@rn-vui/ratings) | [0.5.0 / `05783ad`](https://github.com/deepktp/react-native-vikalp-ratings/tree/05783ad7fa318fbe995082fd71f69c4c572fbc25) | 2,840 | A maintained fork of the familiar swipe/tap API with some newer semantics and tests | -| [`@kolking/react-native-rating`](https://www.npmjs.com/package/@kolking/react-native-rating) | [1.4.1 / `1938275`](https://github.com/kolking/react-native-rating/tree/19382752e7a47cd5370d0e5206fc107ad52be3e2) | 2,135 | Zero dependencies, polished presets, exact decimal display, custom images | -| [`react-native-stars`](https://www.npmjs.com/package/react-native-stars) | [1.2.2 / `8a967ab`](https://github.com/Extrct/react-native-stars/tree/8a967ab17462dd1de3af445dd220da6f9197b2a0) | 1,785 | Simple full/half selection, arbitrary partial display, images or elements | -| [`@aashu-dubey/react-native-rating-bar`](https://www.npmjs.com/package/@aashu-dubey/react-native-rating-bar) | [0.3.6 / `34fb0cf`](https://github.com/Aashu-Dubey/react-native-rating-bar/tree/34fb0cf0c0b38cdf3f8c002d42ef6fc435978175) | 152 | Gesture Handler implementation with horizontal/vertical/reverse and RTL options | -| [`react-native-rating-star`](https://www.npmjs.com/package/react-native-rating-star) | [0.2.1](https://github.com/liuchungui/react-native-star-rating) | 2 | A 2016 image-based result; useful as search-history context, not a modern baseline | - -Primary download endpoint pattern: `https://api.npmjs.org/downloads/point/last-week/`. - -## This repository's open work - -### Issues - -| Item | Verified request | Next-version disposition | -| --- | --- | --- | -| [#4: half-filled stars](https://github.com/f0rr0/react-native-rating/issues/4) | Display and select values such as 2.5 and 4.5 | Close with one numeric tick model. Half, quarter, tenth, and other supported steps use the same selection and fill calculation. | -| [#8: pass an element instead of images](https://github.com/f0rr0/react-native-rating/issues/8) | Use vector icons/components and control their colors | Close with `renderItem`, including `fill`, direction, orientation, state, size, and colors. | -| [#13: disabled indexes or a minimum](https://github.com/f0rr0/react-native-rating/issues/13) | The body explicitly accepts either sparse disabled stars or `min={3}` | Close with `min`. Sparse holes would make slider increment/decrement and drag semantics harder to predict; the verified minimum use case has a coherent model. | -| [#14: ratings in FlatList](https://github.com/f0rr0/react-native-rating/issues/14) | Identify and render independent list-row ratings | Close with controlled row values plus the dedicated `RatingDisplay` static path for review/aggregate lists. | -| [#10: Greenkeeper activation](https://github.com/f0rr0/react-native-rating/issues/10) | Obsolete Greenkeeper service setup | Close as obsolete; current dependency automation and Release Please replace it. | - -### Pull requests - -| Item | State on 2026-07-28 | Decision | -| --- | --- | --- | -| [#9: update rating externally](https://github.com/f0rr0/react-native-rating/pull/9) | Open, conflicting, edits the removed `src/rating.js` architecture | Superseded by the controlled `value` API introduced in v3 and retained here. Do not transplant the old diff. | -| [#11: selected/unselected elements](https://github.com/f0rr0/react-native-rating/pull/11) | Open, conflicting, edits the removed `src/rating.js` architecture | Superseded by the typed `renderItem` API, which also handles fractional fill and interaction state. | -| [#18: Jest dependency group](https://github.com/f0rr0/react-native-rating/pull/18) | Open; dependency review passes, but the main Check fails and the React Native matrix is skipped. The Jest 30 runtime calls `clearMocksOnScope`, which is absent from React Native preset's Jest 29 module mocker. | Structurally mergeable on GitHub, but not mergeable as a validated change today. Do not fold it in merely because it is newer. Reproduce a Jest 30 upgrade only when Jest and the React Native preset/module-mocker generation agree and the full matrices pass. | - -The exact `clearMocksOnScope` failure is preserved in [#18's Check job](https://github.com/f0rr0/react-native-rating/actions/runs/30319643698/job/90152631361). - -The product changes above belong in one next-version pull request. There is no intermediate compatibility component or partial feature release. - -## Competitor implementation and issue audit - -### `react-native-star-rating-widget` - -What works well: - -- A clear split between interactive [`StarRating`](https://github.com/bviebahn/react-native-star-rating-widget/blob/c8906b677061ca7ac252c191419495e8cdefa381/src/StarRating.tsx) and static [`StarRatingDisplay`](https://github.com/bviebahn/react-native-star-rating-widget/blob/c8906b677061ca7ac252c191419495e8cdefa381/src/StarRatingDisplay.tsx). -- Explicit interaction start/end callbacks, a custom icon component, native adjustable actions, animation, and automatic RTL handling. - -Verified limitations and pain: - -- The responder returns true for start, start capture, move, and move capture. That is observable in [`StarRating.tsx`](https://github.com/bviebahn/react-native-star-rating-widget/blob/c8906b677061ca7ac252c191419495e8cdefa381/src/StarRating.tsx#L200-L270) and can compete aggressively with parent scrolling. -- Move calculation reads `nativeEvent.locationX`. React Native documented the moving-target failure in [react-native#15290](https://github.com/react/react-native/issues/15290), and React Native Web documented the same class of problem in [react-native-web#693](https://github.com/necolas/react-native-web/issues/693). -- Selection is limited to the fixed `full`, `half`, and `quarter` modes. -- Every item owns an animation value; the interactive component animates during interaction rather than limiting motion to completion. -- [#78](https://github.com/bviebahn/react-native-star-rating-widget/issues/78) remains open for its Web accessibility prop warning. RTL demand was visible in [#70](https://github.com/bviebahn/react-native-star-rating-widget/issues/70) before automatic support landed. -- Source review suggests repeated controlled drag events can recur while a slow parent still supplies the old `rating`, because deduplication compares against that prop rather than a gesture-local last-emitted tick. This is an inference from source, not a reported issue. - -### `@kolking/react-native-rating` - -What works well: - -- Zero runtime dependencies, polished light/dark variants, exact decimal controlled display, and a compact API. -- It correctly records `pageX - locationX` at grant and uses page coordinates during movement, a sound response to the known `locationX` problem. -- Its README explicitly warns list users about pending callbacks, making the performance tradeoff discoverable. - -Verified limitations and pain: - -- The root responder always accepts start and returns false from termination requests in [`Rating.tsx`](https://github.com/kolking/react-native-rating/blob/19382752e7a47cd5370d0e5206fc107ad52be3e2/src/Rating.tsx#L64-L137). There is no axis-intent phase for a surrounding ScrollView. -- User input resolves with `Math.ceil` to whole items even though controlled visuals can show decimal fill. -- Disabled/list use still constructs root animated values, and every symbol constructs an animation value in [`RatingSymbol.tsx`](https://github.com/kolking/react-native-rating/blob/19382752e7a47cd5370d0e5206fc107ad52be3e2/src/RatingSymbol.tsx). -- The audited source has no adjustable accessibility node, Web slider semantics, or keyboard handler. -- Reported pain includes coordinate handling [#14](https://github.com/kolking/react-native-rating/issues/14) and [#19](https://github.com/kolking/react-native-rating/issues/19), large-list performance [#15](https://github.com/kolking/react-native-rating/issues/15), and reverse-order demand [#35](https://github.com/kolking/react-native-rating/issues/35). These issues are closed, but they validate the categories. - -### `react-native-ratings` - -What works well: - -- A mature, recognizable API with separate tap and swipe experiences, built-in star/heart/rocket/bell images, custom images, start/swipe/finish callbacks, fractions, minimum values, and Airbnb-style review labels. -- Its long adoption history proves demand for both simple star input and descriptive feedback choices. - -Verified limitations and pain: - -- The audited 8.1.0 [`SwipeRating`](https://github.com/Monte9/react-native-ratings/blob/0a8f16b4380626556b636e8883b06c0db9e885ec/src/SwipeRating.tsx) creates a PanResponder and new animated objects through its interaction path, measures against window coordinates, and keeps read-only behavior inside the same component. -- Fractional callback and visual behavior diverges for `jumpValue` in open [#155](https://github.com/Monte9/react-native-ratings/issues/155). -- Invalid layout math can reach a native `NaN` width crash: [#183](https://github.com/Monte9/react-native-ratings/issues/183). -- Custom/background image rendering has open reports in [#167](https://github.com/Monte9/react-native-ratings/issues/167), [#175](https://github.com/Monte9/react-native-ratings/issues/175), and [#197](https://github.com/Monte9/react-native-ratings/issues/197). -- Accessibility is still requested in [#172](https://github.com/Monte9/react-native-ratings/issues/172), RTL is still open in [#81](https://github.com/Monte9/react-native-ratings/issues/81), and a negative-to-positive/bidirectional scale is requested in [#189](https://github.com/Monte9/react-native-ratings/issues/189). - -These are the strongest issue-backed opportunities because they span correctness, crashes, rendering reliability, accessibility, international layout, and a new semantic job—not cosmetic preference alone. - -### React Native Elements Rating - -What works well: - -- It is discoverable inside a broad design system with current releases and a familiar API. -- The current [`Rating`](https://github.com/react-native-elements/react-native-elements/blob/32ae0eda7c559f55a9d858fc6880ed46f7714c5b/packages/base/src/Rating/Rating.tsx) is small and delegates to the toolkit's swipe-rating implementation. - -Verified limitations: - -- Installing a UI toolkit is a different dependency decision from adopting a focused core-only control. -- The delegated [`SwipeRating`](https://github.com/react-native-elements/react-native-elements/blob/32ae0eda7c559f55a9d858fc6880ed46f7714c5b/packages/base/src/AirbnbRating/SwipeRating.tsx) retains window-relative measurement and immediate PanResponder acceptance. -- It does not provide a generic semantic scale or a dedicated exact aggregate display component. - -### `@rn-vui/ratings` - -What works well: - -- It modernizes the familiar tap/swipe API with hooks, TypeScript, current tests, and some accessibility state/value coverage. - -Verified limitations: - -- Its [`SwipeRating`](https://github.com/deepktp/react-native-vikalp-ratings/blob/05783ad7fa318fbe995082fd71f69c4c572fbc25/src/SwipeRating.tsx) adds an Animated listener during render, accepts the responder immediately, and intentionally suppresses hook dependencies around interaction callbacks. Dynamic configuration/callback freshness is therefore risky. -- It retains image masking, window-relative measurement, and the older fractions/jump model. -- Static/read-only content remains in the same swipe implementation rather than a minimal display path. - -### `react-native-stars` - -What works well: - -- A small API, arbitrary partial display, half-star selection, and support for images or React elements. - -Verified limitations: - -- The [1.2.2 source](https://github.com/Extrct/react-native-stars/blob/8a967ab17462dd1de3af445dd220da6f9197b2a0/index.js) uses class lifecycle code, loose equality, and `value || fallback` selection. A legitimate controlled zero can therefore be replaced by a legacy prop. -- Half selection creates two touchables per star; full selection creates one per star. There is no single slider semantic, drag input, keyboard path, RTL model, or TypeScript contract. -- Its last source release predates the current React 19/React Native architecture. - -### `@aashu-dubey/react-native-rating-bar` - -What works well: - -- The widest layout feature set in the focused competitors: Gesture Handler, tap and pan, half values, minimum/maximum, horizontal, vertical, vertical-reverse, explicit layout direction, and custom elements. - -Verified limitations: - -- It requires `react-native-gesture-handler`, which is a reasonable choice for complex gesture systems but a meaningful cost for a small form control. -- The audited [`RatingBar.tsx`](https://github.com/Aashu-Dubey/react-native-rating-bar/blob/34fb0cf0c0b38cdf3f8c002d42ef6fc435978175/src/RatingBar.tsx) contains parallel RTL/platform coordinate branches, cloned-element state, and a source TODO noting double-render slowdown during drag callbacks. -- It supports only whole/half selection, and the audited source has no complete adjustable/ARIA keyboard model. - -### Older first-page/second-page results - -`react-native-star-rating` and `react-native-rating-star` still capture generic search traffic despite 2018 and 2016 latest releases. Their presence is an SEO lesson: an exact package name, a plain-language title, installation snippet, and stable repository history can rank for years. Their image/vector-icon installation, per-star press targets, and older React APIs are not an implementation baseline for the next version. - -## What to borrow - -These are product lessons, not copied source: - -| Lesson | Evidence | Adaptation here | -| --- | --- | --- | -| Separate interactive and static jobs | `react-native-star-rating-widget` has `StarRatingDisplay`; FlatList demand appears in local #14 | First-class `RatingDisplay`, exact by default and free of interaction allocations | -| Make interaction lifecycle explicit | Widget and Monte packages expose start/move/end phases | `onInteractionStart`, distinct `onChange`, and one `onChangeEnd` with source/cancellation | -| Custom visuals are table stakes | Widget icon component, Kolking symbols, Monte custom image, local #8 | Typed render slots that also expose fractional fill, direction, and pressed state | -| RTL and vertical layout must be intentional | Widget/Kolking RTL work, Aashu direction matrix, multiple RTL issues | `direction`, `orientation`, and semantic `reversed` are separate concepts | -| Descriptive feedback is more valuable than stars alone | Airbnb review labels and Monte #189 | Generic `RatingScale` for NPS, Likert, emoji, zero, negatives, and strings | -| A focused component should be easy to adopt | Kolking's zero-dependency pitch | Core React Native implementation with no required icon, SVG, or gesture package | -| Arbitrary aggregate display matters | Kolking and `react-native-stars` render decimals | Exact `RatingDisplay` fill, while interactive snapping remains explicit | - -## What this package can offer that the audited hits cannot - -The defensible differentiation is the combination, not a longer prop list: - -1. **Scroll-aware drag intent.** One root state machine waits for slop and primary-axis dominance, permits responder termination before lock, and retains only a deliberate rating drag. -2. **One value model end to end.** Integer ticks drive pointer selection, keyboard actions, native accessibility values, callback deduplication, and fractional visual fill. This directly targets the verified visual/callback divergence class. -3. **Accessibility parity, not a label.** One native adjustable control, direct Web ARIA slider attributes, standard keyboard keys, localized value text, visible focus, non-color-only star shapes, and reduced-motion behavior. -4. **A real static architecture.** `RatingDisplay` and read-only scales do not merely disable an interactive implementation. That directly serves FlatList/SectionList use. -5. **Semantic ratings.** `RatingScale` does not overload zero as “empty,” so NPS zero and negative-to-positive scales work without sentinel collisions. -6. **Independent layout semantics.** Locale direction, interaction orientation, bottom-to-top vertical progression, fill origin, and semantic reversal are explicit and testable. `reversed` remaps semantic items rather than adding conflicting coordinate branches. -7. **Core-only customization.** The default works without setup, while render slots accept the user's existing icon/SVG/design system instead of choosing one for them. -8. **Defensive boundaries.** Invalid values, counts, sizes, steps, duplicate scale items, gaps, and measured extents normalize before reaching native styles, targeting the verified `NaN` crash class. -9. **Semantic content that can fit.** `itemExtent` gives horizontal labels more primary-axis room without inflating visual size or the cross-axis target, while the same value is available to custom renderers. -10. **Intentional duplicate semantics.** Static aggregate and read-only scale visuals can be marked `decorative` when adjacent text already communicates their value; interactive controls remain one focusable slider. - -## Positioning and SEO plan - -### Search promise - -Lead with one sentence everywhere: - -> Accessible React Native star rating, drag rating, and semantic feedback scale for iOS, Android, Expo, and React Native Web. - -The supporting proof line is: fractional, RTL, Web keyboard-ready, and zero runtime dependencies. - -It contains the high-intent terms without keyword stuffing: - -- React Native rating component -- React Native star rating -- React Native drag/swipe rating -- Expo rating component -- React Native Web rating -- accessible/keyboard rating -- fractional/half-star rating -- RTL/vertical rating -- NPS, Likert, and emoji rating scale -- FlatList rating display - -### Proof before superlatives - -Do not call the package “the best” as an unsupported claim. Show the difference: - -- a short clip of a horizontal rating inside a vertical ScrollView; -- keyboard + two-color focus-indicator interaction in the Web example; -- VoiceOver/TalkBack adjustable actions; -- exact `4.37` display in a large FlatList; -- LTR, RTL, vertical, and reversed semantic scale in one layout; -- NPS `0` and a `-2…2` scale proving the null-sentinel design; -- reduced-motion on/off behavior. - -The README comparison must stay versioned and sourced. Re-audit it for every major release rather than letting competitor claims go stale. - -### Conversion path - -1. The npm/GitHub fold answers compatibility, install, and a controlled five-star example. -2. The next screen proves the unique jobs: drag, `RatingDisplay`, and `RatingScale`. -3. Copy-paste examples cover the exact search intents above. -4. Callback documentation tells teams where to put state versus persistence. -5. The architecture document provides trust for maintainers evaluating gesture correctness, performance, and accessibility. - -### Release launch - -- Publish one integrated next version and one changelog story; do not fragment the message across interim releases. -- Create a concise demonstration asset and deploy a live Expo/Web example before adding a public link. The repository currently provides the local Expo example plus packed-package Web export and server-render smoke coverage; it does not yet claim a deployed demo URL. -- Announce the issue-backed outcomes: half/fractional fill, custom components, minimum ratings, FlatList display, RTL, accessibility, and semantic scales. -- Link the resolved local issues and thank the original reporters. -- Share factual implementation notes with React Native/Expo communities: coordinate math, ScrollView coexistence, Web slider semantics, and the static-path performance design are useful even to non-users. - -### Measures - -Capture a release-day baseline and review at 7, 30, and 90 days: - -- npm weekly downloads and dependent count; -- GitHub clone/traffic, stars, and README-to-install conversion where available; -- search position for the query families in the audit; -- example visits and completion of the live rating demo once a public deployment exists; -- issue mix: defects versus setup questions versus feature requests; -- bundle/package size and list-render benchmark regressions. - -Growth is not evidence of correctness. Accessibility checks, package compatibility, issue recurrence, and crash-free usage remain release guardrails. - -## Primary implementation guidance - -The product decisions were checked against current primary guidance: - -- [React Native Gesture Responder System](https://reactnative.dev/docs/gesture-responder-system) -- [React Native PanResponder](https://reactnative.dev/docs/panresponder) -- [React Native Animated](https://reactnative.dev/docs/animated) -- [React Native performance overview](https://reactnative.dev/docs/performance) -- [React Native accessibility](https://reactnative.dev/docs/accessibility) -- [React Native AccessibilityInfo](https://reactnative.dev/docs/accessibilityinfo) -- [React Native I18nManager](https://reactnative.dev/docs/i18nmanager) -- [React Native Web interactions](https://necolas.github.io/react-native-web/docs/interactions/) -- [React Native Web accessibility](https://necolas.github.io/react-native-web/docs/accessibility/) -- [WAI-ARIA Authoring Practices slider pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider/) -- [Apple accessibility guidance](https://developer.apple.com/design/human-interface-guidelines/accessibility) -- [Android accessible view guidance](https://developer.android.com/guide/topics/ui/accessibility/views/apps-views?hl=en) -- [WCAG 2.2 non-text contrast](https://www.w3.org/TR/WCAG22/#non-text-contrast) -- [WCAG 2.2 Focus Appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html) - -The corresponding implementation invariants and release gates live in [ARCHITECTURE.md](./ARCHITECTURE.md). diff --git a/package.json b/package.json index e0f9de6..01289e5 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "files": [ "lib", "src", - "docs", "CHANGELOG.md", "LICENSE.md", "README.md"