Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,26 @@ jobs:
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Cache cocoapods
id: cocoapods-cache
if: env.turbo_cache_hit != 1
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
example/ios/Pods
~/.cocoapods
key: ${{ runner.os }}-pods-${{ hashFiles('example/ios/Podfile.lock', 'example/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-

- name: Install cocoapods
if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
if: env.turbo_cache_hit != 1
run: |
cd example
bundle install
bundle exec pod repo update --verbose
if [[ "${{ steps.cocoapods-cache.outputs.cache-hit }}" != "true" ]]; then
bundle exec pod repo update --verbose
fi
bundle exec pod install --project-directory=ios

- name: Build example for iOS
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Accessibility: set `accessibilityState.busy` automatically while `loading={true}`
- Reduce Motion: render a static `baseColor` skeleton when the system setting is enabled (no shimmer animation)
- DEV warning when `loading` is omitted but `children` are present (defaults to `true`)
- Example app: Reduce Motion banner, wrapped Lines demo, accessibility labels

### Changed

- `GleamView.Line` nested inside intermediate wrappers (e.g. `<View>`) is detected on the first render — stable ref, no NativeGleamView→View flip

### Fixed

- CI: add missing CocoaPods cache step referenced by the iOS build job
- CI: always run `pod install` after cache restore (codegen needs fresh `build/generated`)
- CI: stop caching `vendor/bundle` (stale gems broke Ruby 3.4 pod install)
- Example: add `nkf` and `base64` gems for Ruby 3.4 / CocoaPods compatibility

## [1.0.6] - 2026-05-15

### Fixed
Expand Down
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ When `loading={true}`, each `GleamView.Line` renders its own shimmer bar, sized

Lines inherit `loading`, `speed`, `direction`, `baseColor`, `highlightColor`, `intensity`, `transitionDuration`, and `transitionType` from the parent. `delay` and `onTransitionEnd` are per-line.

For best performance, place `GleamView.Line` as direct children of `GleamView` (or inside fragments). Lines nested inside intermediate wrappers (e.g., `<View>`) still work, but require an extra render cycle to detect.
For best performance, place `GleamView.Line` as direct children of `GleamView` (or inside fragments). Lines nested inside intermediate wrappers (e.g., `<View>`) are detected automatically on the first render.

Every `GleamView` provides context to its subtree. A `GleamView.Line` always binds to its nearest `GleamView` ancestor — nested `GleamView` components each control their own Lines independently.

Expand Down Expand Up @@ -196,6 +196,27 @@ The shimmer respects uniform `borderRadius` and standard view styles.

- The shimmer overlay supports uniform `borderRadius` only — per-corner radii are not applied to the shimmer.

## Accessibility

When `loading={true}`, `GleamView` and `GleamView.Line` automatically set `accessibilityState={{ busy: true }}` (merged with any existing `accessibilityState` you pass).

When the system **Reduce Motion** setting is enabled, shimmer animation is replaced by a static `baseColor` placeholder — no native animation runs until content loads.

## Example app

The [`example/`](example/) app includes interactive demos for all props plus:

- **Reduce Motion** — live status banner (toggle in system accessibility settings)
- **Wrapped Lines** — `GleamView.Line` nested inside a `<View>` wrapper
- **Accessibility** — `accessibilityLabel` / `accessibilityRole` on lines; `accessibilityState.busy` while loading

Run from the repo root:

```sh
yarn example start
yarn example ios # or: yarn example android
```

## License

MIT
2 changes: 2 additions & 0 deletions example/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'
gem 'base64'
gem 'nkf'
3 changes: 3 additions & 0 deletions example/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ GEM
minitest (5.27.0)
molinillo (0.8.0)
mutex_m (0.3.0)
nkf (0.3.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
Expand All @@ -106,12 +107,14 @@ PLATFORMS

DEPENDENCIES
activesupport (>= 6.1.7.5, != 7.1.0)
base64
benchmark
bigdecimal
cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
concurrent-ruby (< 1.3.4)
logger
mutex_m
nkf
xcodeproj (< 1.26.0)

RUBY VERSION
Expand Down
136 changes: 135 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import {
AccessibilityInfo,
FlatList,
StyleSheet,
Text,
Expand Down Expand Up @@ -88,17 +89,77 @@ export default function App() {
const [colorIndex, setColorIndex] = useState(0);
const [transitionDuration, setTransitionDuration] = useState(300);
const [transitionType, setTransitionType] = useState(GleamTransition.Fade);
const [reduceMotion, setReduceMotion] = useState(false);

useEffect(() => {
let mounted = true;

AccessibilityInfo.isReduceMotionEnabled()
.then((enabled) => {
if (mounted) {
setReduceMotion(enabled);
}
})
.catch(() => {});

const subscription = AccessibilityInfo.addEventListener(
'reduceMotionChanged',
setReduceMotion
);

return () => {
mounted = false;
subscription.remove();
};
}, []);

const direction = DIRECTIONS[dirIndex]!.value;
const colors = COLOR_PRESETS[colorIndex]!;

const sharedGleamProps = useMemo(
() => ({
loading,
speed,
intensity,
direction,
baseColor: colors.base,
highlightColor: colors.highlight,
transitionDuration,
transitionType,
}),
[
loading,
speed,
intensity,
direction,
colors.base,
colors.highlight,
transitionDuration,
transitionType,
]
);

const clamp = (v: number, min: number, max: number) =>
Math.min(max, Math.max(min, v));

return (
<ScrollView style={styles.scroll} contentContainerStyle={styles.container}>
<Text style={styles.title}>react-native-gleam</Text>

<View
style={[
styles.callout,
reduceMotion ? styles.calloutActive : styles.calloutMuted,
]}
>
<Text style={styles.calloutTitle}>Reduce Motion</Text>
<Text style={styles.calloutText}>
{reduceMotion
? 'Enabled — GleamView shows a static baseColor skeleton (no shimmer).'
: 'Disabled — toggle in iOS Settings → Accessibility → Motion, or Android Remove animations.'}
</Text>
</View>

{/* Profile card skeleton */}
<View style={styles.profileCard}>
<View style={styles.profileHeader}>
Expand All @@ -112,6 +173,8 @@ export default function App() {
transitionDuration={transitionDuration}
transitionType={transitionType}
style={styles.avatar}
accessibilityLabel="User avatar"
accessibilityRole="image"
>
<View style={styles.avatarContent}>
<Text style={styles.avatarText}>JD</Text>
Expand Down Expand Up @@ -304,6 +367,43 @@ export default function App() {
</GleamView>
</View>

{/* Lines inside a View wrapper + accessibility labels */}
<Text style={styles.sectionLabel}>Wrapped Lines + accessibility</Text>
<Text style={styles.sectionHint}>
GleamView.Line inside a View wrapper (first-render detection). Each line
sets accessibilityLabel; busy is applied automatically while loading.
</Text>
<View style={styles.lineCard}>
<GleamView {...sharedGleamProps}>
<View style={styles.wrappedLines}>
<GleamView.Line
style={styles.lineTitle}
delay={0}
accessibilityLabel="Loading product name"
accessibilityRole="text"
>
<Text style={styles.lineTitleText}>Premium Plan</Text>
</GleamView.Line>
<GleamView.Line
style={styles.lineSubtitle}
delay={120}
accessibilityLabel="Loading price"
accessibilityRole="text"
>
<Text style={styles.lineSubtitleText}>$12 / month</Text>
</GleamView.Line>
<GleamView.Line
style={styles.lineBodyShort}
delay={240}
accessibilityLabel="Loading description"
accessibilityRole="text"
>
<Text style={styles.lineBodyText}>Includes all features.</Text>
</GleamView.Line>
</View>
</GleamView>
</View>

{/* Staggered demo */}
<Text style={styles.sectionLabel}>Staggered</Text>
<View style={styles.staggered}>
Expand Down Expand Up @@ -400,6 +500,31 @@ const styles = StyleSheet.create({
marginBottom: 20,
textAlign: 'center',
},
callout: {
borderRadius: 12,
padding: 12,
marginBottom: 20,
borderWidth: 1,
},
calloutActive: {
backgroundColor: '#FFF8E6',
borderColor: '#F0D48A',
},
calloutMuted: {
backgroundColor: '#FFFFFF',
borderColor: '#E5E5E5',
},
calloutTitle: {
fontSize: 13,
fontWeight: '700',
color: '#444',
marginBottom: 4,
},
calloutText: {
fontSize: 12,
color: '#666',
lineHeight: 18,
},
profileCard: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
Expand Down Expand Up @@ -606,6 +731,15 @@ const styles = StyleSheet.create({
color: '#555',
marginBottom: 8,
},
sectionHint: {
fontSize: 12,
color: '#888',
lineHeight: 18,
marginBottom: 10,
},
wrappedLines: {
gap: 8,
},
staggered: {
gap: 6,
marginBottom: 24,
Expand Down
2 changes: 1 addition & 1 deletion lib/module/GleamContext.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 24 additions & 1 deletion lib/module/GleamLine.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/module/GleamLine.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions lib/module/gleamAccessibility.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/module/gleamAccessibility.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading