diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12084bf..8349eff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,3 +72,19 @@ jobs: - name: Validate Expo config run: pnpm run example:build + + build-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3 + + - name: Setup + uses: ./.github/actions/setup + + - name: Check documentation + run: pnpm run docs:check + + - name: Build documentation + run: pnpm run docs:build diff --git a/.oxfmtrc.json b/.oxfmtrc.json index ded30a7..8b06430 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -1,6 +1,16 @@ { "$schema": "./node_modules/oxfmt/configuration_schema.json", - "ignorePatterns": ["lib", "node_modules", "ios", "android", "dist", "**/*.md"], + "ignorePatterns": [ + "lib", + "node_modules", + "ios", + "android", + "dist", + "doc_build", + ".rspress", + "**/*.md", + "**/*.mdx" + ], "sortImports": { "groups": [ "type-import", diff --git a/.oxlintrc.json b/.oxlintrc.json index ff2449c..90d9ec5 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -58,5 +58,5 @@ "globals": { "__DEV__": "readonly" }, - "ignorePatterns": ["lib", "node_modules", "ios", "android", "dist"] + "ignorePatterns": ["lib", "node_modules", "ios", "android", "dist", "doc_build", ".rspress"] } diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..c4e73d7 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,118 @@ +# Design + +## Source of truth + +Status: Active. Updated: 2026-10-31. Scope: the inline documentation demos only. +Evidence: the user's supplied demo screenshot; `docs/components/MotionDemo.tsx`, +`docs/components/motion-demo.css`, `example/src/DocsDemo.tsx`, the Rspress theme, +and the existing English/Korean documentation. The user rejected the large navy +panel, mint emphasis, duplicate labels, and conspicuous decorative chrome. + +## Brand + +Quiet, precise, developer-facing documentation. Keep the existing Motion Kit +rabbit asset. Avoid gradients, promotional copy, bright status badges, pill-like +code labels, heavy cards, and extra fonts or icon packages. + +## Product goals + +Let readers compare text motion, replay it, inspect the actual recipe, and see +timeline progress independently of the Typewriter character count. Success means +the animated text is the focus and all existing controls remain easy to use. +Compare stagger directions with the displayed recipe following the selection. +Show native line remeasurement when a paragraph switches between 220 and 320 +layout units, including a fixed halfway progress state. Library semantics, +documentation routes, and the site home page are outside scope. + +## Personas and jobs + +React Native developers reading an API page, comparing an effect, and copying its +source. They use desktop browsers and narrow mobile documentation layouts. + +## Information architecture + +One compact example: a single name and platform label, an animation stage, local +playback controls, then a thin toolbar for source disclosure and closing the live +preview. Source remains below its own example, with recipe/player selections. + +## Design principles + +- Show the demo name once; do not repeat it inside an embedded player. +- Use document surfaces and neutral controls so motion supplies the emphasis. +- Keep controls near the text and remove excess vertical space. +- Preserve an explicit React Native Web versus iOS recording distinction. +- Keep existing library behavior and source accuracy when adding comparisons. + +## Visual language + +Inherit Rspress text, surface, divider, and focus colors. Use matching neutral +light/dark palettes in the isolated RNW player. Use the system font and existing +code font, 24–26px medium-weight example text, 12–13px control text, 4/8px spacing, +a single thin border, 6px corners, and no shadow or gradient. Ordinary stages are +about 240px high; control/progress examples have room for their extra readouts. +Do not apply the design-search suggestions for oversized display typography, +large whitespace, page transitions, or new font downloads. + +## Components + +`MotionDemo` owns framing, platform information, theme synchronization, source +viewing, and preview lifecycle. `DocsDemo` owns the actual library rendering, +playback actions, and progress/count diagnostics. Reuse Rspress syntax highlighting +and copy controls. Keep palette values local to the preview implementation. +For stagger, `MotionDemo` owns a four-button direction group above the preview; +the selected recipe is shared by the iframe query and generated source. Selecting +a direction starts that recipe from the beginning. Native width reflow stays an +iOS recording with real Narrow/Wide and progress controls visible in the capture. + +## Accessibility + +Maintain keyboard-operable buttons, visible focus, descriptive source disclosure, +and contrast in both themes. Preserve reduced-motion behavior and the library's +accessible full-text label. Use at least 44px control hit areas where practical; +selected progress is indicated by both an outline and accessible state. No new +autoplay loops, decorative animation, or per-frame screen-reader announcements. + +## Responsive behavior + +At 375px, wrap control groups without horizontal page scrolling or clipped +readouts. Keep all Typewriter progress/count information visible. Wide layouts +stay within the document column rather than becoming a dashboard or hero section. +Check a narrow phone viewport and landscape, plus a desktop viewport. + +## Interaction states + +The initial state offers a neutral Run example button. Loading and source loading +remain legible. Replay/reset/stop/progress keep their existing meaning. Closing a +live preview unmounts it. Toggling source does not remount the animation. Theme +updates reach the iframe without resetting playback. Native recordings use English +sample text and matching light/dark captures with native video controls. Switching +appearance selects the corresponding recording. +Stagger selection is available before running the example and while viewing code. +Source disclosure and source tabs preserve the selection. Each embedded demo owns +its own direction state. Theme changes preserve that choice. +Before playback, show its poster and a neutral Play recording button; expose +native video controls after the user starts it. + +## Content voice + +Short functional English/Korean labels: Run example, Replay, Code, Close preview, +Timeline progress, Visible characters. Platform metadata is muted sentence case, +not a colored LIVE badge. Avoid repeated implementation explanations in toolbars. + +## Implementation constraints + +Use the current Rspress, Expo, React Native Web, and Reanimated setup. Add no +dependencies and change no `packages/text-motion/src` files. Keep source extraction +and the genuine iOS recording intact. Theme messages contain appearance data only +and the embedded app accepts messages only from its parent. + +Implementation plan: retain the supplied screenshot as the visual baseline; +update the wrapper and player presentation independently; then run source checks, +typechecking, lint/format, and a production docs build. Verify replay/source/close, +Typewriter 0 → 25 → 50 → 0 (0/10 → 9/10 → 10/10 → 0/10), continuous playback, +English/Korean labels, light/dark appearance, and narrow-layout screenshots. + +## Open questions + +None blocking. Native recordings are identified as captures; this change does not +promise browser/native rendering equivalence. diff --git a/README.ko.md b/README.ko.md index e550ee1..d0ae5f2 100644 --- a/README.ko.md +++ b/README.ko.md @@ -1,5 +1,7 @@ # @react-native-motion-kit/text-motion +한영 Rspress 문서: [개발 및 빌드 안내](./docs/README.md). `pnpm docs:dev`로 로컬 문서를 실행할 수 있습니다. + React Native에서 고성능 split text animation을 만들기 위한 라이브러리입니다. Reanimated 기반으로 동작하고, 확장 가능한 renderer boundary를 염두에 두고 있습니다. English documentation: [README.md](./README.md) diff --git a/README.md b/README.md index 8319169..32f7da6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Korean documentation: [README.ko.md](./README.ko.md) Package documentation: [packages/text-motion/README.md](./packages/text-motion/README.md) +Bilingual Rspress documentation: [development and build instructions](./docs/README.md). Run `pnpm docs:dev` to browse locally. + This repository is a pnpm workspace for the `@react-native-motion-kit/text-motion` package and Expo example app. ## Package diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..02875e3 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +doc_build/ +.rspress/ +.cache/ +docs/public/demos/ +*.local +*.log diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..6618356 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,63 @@ +# React Native Text Motion documentation + +This pnpm workspace uses Rspress 2, following the structure of the Gesture Image +Viewer documentation. It documents the current public `0.1.0` API in English and +Korean, with one set of routes and no historical version selector. + +## Develop + +Run commands from the repository root: + +```sh +pnpm install +pnpm docs:dev +``` + +English is served at `/`; Korean is served at `/ko/`. Use the language menu to +switch to the corresponding page. The home page extends the default theme with +package-manager installation tabs. + +## Verify and build + +```sh +pnpm docs:check +pnpm docs:typecheck +pnpm docs:build +pnpm docs:preview +``` + +The static build is written to `docs/doc_build/`. A static host such as Cloudflare +Pages can use `pnpm docs:build` as its build command and `docs/doc_build` as its +output directory, with the repository root as the working directory. Deployment +and a production domain must be configured separately. + +## Maintain + +- Keep matching pages under `docs/en/` and `docs/ko/` in sync. `docs:check` checks + page parity, local links, and navigation metadata. +- Edit `_nav.json` for top navigation and `_meta.json` for sidebar ordering. +- Keep code samples aligned with the public exports and the Expo example app. +- Use `docs/public/` for shared static files and `theme/` for theme extensions. +- Rspress generates `llms.txt` and full documentation text for AI tools. +- Link to official setup instructions for Reanimated and Worklets; the package's + peer minimums are not a complete native dependency compatibility matrix. +- Inline demos run the actual library using an isolated React Native Web bundle. + Press Run example to load one, use its replay/controls, and close it to unload. + Reduced-motion preferences remain respected. Browser playback illustrates the + API; it is not native performance evidence. +- Open **View code** below an example to inspect and copy that demo's + recipe, including its shared defaults and text. The second view contains the + complete shared player source for playback handlers and styles. These views + are extracted from the same Expo source files during development, checks, + typechecking, and production builds, rather than maintained as separate samples. +- The preview follows the document's light/dark appearance without restarting + playback. The wrapper and embedded app share appearance data only. See the + repository's `DESIGN.md` for the documentation demo's visual constraints. +- `pnpm docs:dev` and `pnpm docs:build` first export the demo app from `example/` + into ignored `docs/public/demos/`. After changing preview recipes during a dev + session, run `pnpm -C docs demos:build` again and reopen the preview. +- The native `overlayText()` / `lineReveal()` demo uses an iOS simulator recording + in `docs/public/media/`, because React Native Web does not provide the native + `onTextLayout` event required for rendered-line measurement. +- Native recordings use English text with matching light/dark palettes. The + video and poster follow the document theme, just like the live preview shell. diff --git a/docs/components/MotionDemo.tsx b/docs/components/MotionDemo.tsx new file mode 100644 index 0000000..60bf81c --- /dev/null +++ b/docs/components/MotionDemo.tsx @@ -0,0 +1,294 @@ +// oxlint-disable-next-line import/no-unassigned-import -- Styles shared by the documentation preview cards. +import './motion-demo.css'; +import { useDark, useLang, withBase } from '@rspress/core/runtime'; +import { lazy, Suspense, useEffect, useId, useRef, useState } from 'react'; + +import demoSources from '../.cache/demo-sources.json'; +import demos from '../demos.json'; + +type DemoId = keyof typeof demos; +type SourceTab = 'recipe' | 'player'; + +const RuntimeCode = lazy(async () => ({ + default: (await import('@rspress/core/theme-original')).CodeBlockRuntime, +})); + +function NativeDemoVideo({ + id, + theme, + lang, +}: { + id: 'line-reveal' | 'width-reflow'; + theme: 'dark' | 'light'; + lang: 'en' | 'ko'; +}) { + const videoRef = useRef(null); + const [mounted, setMounted] = useState(false); + const [started, setStarted] = useState(false); + const [failed, setFailed] = useState(false); + const mediaPath = `/media/${id}${theme === 'light' ? '-light' : ''}`; + + // Match the SSG placeholder during hydration, then select the browser theme. + useEffect(() => setMounted(true), []); + + if (!mounted) return