Skip to content

feat: add web support via CSS transitions#3

Merged
janicduplessis merged 13 commits intomainfrom
feat/web-support
Mar 16, 2026
Merged

feat: add web support via CSS transitions#3
janicduplessis merged 13 commits intomainfrom
feat/web-support

Conversation

@janicduplessis
Copy link
Collaborator

@janicduplessis janicduplessis commented Mar 14, 2026

Web Support

Adds a .web.tsx platform-specific implementation of EaseView that uses CSS transitions and @keyframes animations — no native modules, zero JS thread overhead.

How it works

  • One-shot animations (fade, slide, rotate, etc.) → CSS transition set imperatively on the DOM element via el.style.transition
  • Loop animations (pulse, scrolling banner) → CSS @keyframes with a dynamically injected <style> tag
  • Uses View from react-native (react-native-web) instead of a raw <div> — handles StyleSheet flattening, RN transform arrays, and style types automatically
  • CSS-only properties (transition, animation, transformOrigin) are set imperatively via a DOM ref since they aren't available as RN style props

Example app changes

  • Added Expo for web support (expo, react-native-web, @expo/metro-runtime)
  • AppRegistry.registerComponent for native + runApplication for web
  • useWindowDimensions() for responsive banner width
  • Added dom lib to tsconfig for DOM types in .web.tsx

Demo — Web (all 17 examples)

web-demo.mp4

Implement EaseView.web.tsx that uses CSS transitions for timing animations,
CSS @Keyframes for loop animations, and a timing approximation for springs.
React Native's .web.tsx resolution automatically picks this up on web.
Install expo and web dependencies (react-dom, react-native-web,
@expo/metro-runtime) to enable running the example app on web
via `npx expo start --web`.
The web EaseView receives RN StyleSheet numeric IDs and style arrays,
but was trying to spread them directly as React.CSSProperties. Spreading
an array gives {0: id} which caused:
  TypeError: Failed to set an indexed property [0] on CSSStyleDeclaration

Fix: use StyleSheet.flatten() to resolve any RN StyleProp to a plain
object before spreading into computedStyle.
Replace the raw <div> with react-native View which:
- Handles StyleSheet flattening automatically (no more manual flattenStyle)
- Accepts StyleProp<ViewStyle> natively
- Converts RN transform arrays to CSS transforms via react-native-web
- Properly handles all RN style types (numeric IDs, arrays, objects)

CSS transitions and keyframe animations are applied imperatively via
the DOM ref, since View does not expose those as style props.

Also removes accidentally committed .playwright-cli artifacts.
…keyframes

Only include border-radius and background-color in CSS @Keyframes
when the user explicitly sets them in animate/initialAnimate props.
Previously, IDENTITY defaults (borderRadius: 0) were always included,
which overrode values set via the style prop (e.g. pulse's borderRadius: 30).
will-change: transform causes the element to be promoted to its own
GPU compositing layer, which can escape parent overflow:hidden clipping
in some browsers. CSS animations already get GPU-promoted automatically,
so the hint is unnecessary.
BANNER_WIDTH was computed at module load time using Dimensions.get('window')
which bakes in the build-time window width on static web exports. Switched
to useWindowDimensions() hook so the banner correctly sizes to the actual
viewport width at render time.
@janicduplessis janicduplessis merged commit f64c614 into main Mar 16, 2026
4 checks passed
@janicduplessis janicduplessis deleted the feat/web-support branch March 16, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant