Atom
ShinyText
Requires <UIProvider> — Getting Started →
Installation
Install just this component (copies the source into your project, no npm dependency)
npx orn-ui add shiny-textOr install the whole package and import it
pnpm add orn-ui
import { ShinyText } from 'orn-ui/shiny-text';Usage
- when to use — A band of light sweeps the text to say "look here" without a badge or an icon: an upgrade card, a beta label, the one headline on a landing screen. One per screen — two competing sweeps say nothing.
- how it works — The text is split into characters and each one's opacity is driven from a single shared value, so the whole effect runs on the native thread. It needs the characters: text takes a string, not nodes.
- color / shineColor — The resting colour and the band that travels across it. Both default to theme roles, so the effect follows a custom brand without being told.
- spread — How wide the bright band is, as a fraction of the text. Small is a glint on one or two characters; large lights most of the line at once and reads as a pulse.
- animated — False renders plain text in shineColor — the escape hatch for a user who asked for less motion, and the state to use in a snapshot test.
Demo clip not recorded yet — see MEDIA.md
Variants
align="center" — wraps by word on several lines
<ShinyText text="A headline long enough to wrap onto more than one line" align="center" {...strong} />duration and delay — slow sweep, long pause
<ShinyText text="Taking its time" duration={4000} delay={1200} {...strong} />animated={false} — plain text in shineColor, same box
<VariantRow>
<ShinyText text="No sweep at all" animated={false} {...strong} />
</VariantRow>Full demo source
function ColorPairs() {
const colors = useColors();
return (
<View style={{ gap: 16 }}>
<View style={{ gap: 4 }}>
<Caption>default — textLight resting, primary shining</Caption>
<ShinyText text="Default pair" />
</View>
<View style={{ gap: 4 }}>
<Caption>border → text — the widest pair the theme has</Caption>
<ShinyText text="Maximum contrast" color={colors.border} shineColor={colors.text} />
</View>
</View>
);
}
function Spreads() {
const colors = useColors();
return (
<View style={{ gap: 12 }}>
<ShinyText text="Narrow glint" spread={0.08} color={colors.border} shineColor={colors.text} />
<ShinyText text="Soft wide wave" spread={0.6} color={colors.border} shineColor={colors.text} />
</View>
);
}
function UpgradeCard() {
const colors = useColors();
return (
<Card>
<ShinyText text="Pro plan" color={colors.primarySoft} shineColor={colors.primary} />
<Body style={{ marginTop: 4 }}>Unlimited invoices · priority support</Body>
</Card>
);
}
export function ShinyTextDemo() {
const colors = useColors();
// El par por defecto sólo cambia luminancia; para mirar *cómo* se mueve la
// banda conviene el par más separado del tema, que cambia tono también.
const strong = { color: colors.border, shineColor: colors.text };
const variants: VariantDef[] = [
{
label: 'colors — the resting/shine pair is what makes it readable',
content: <ColorPairs />,
},
{
label: 'align="center" — wraps by word on several lines',
content: (
<ShinyText text="A headline long enough to wrap onto more than one line" align="center" {...strong} />
),
},
{
label: 'spread — narrow glint vs. soft wave',
content: <Spreads />,
},
{
label: 'duration and delay — slow sweep, long pause',
content: <ShinyText text="Taking its time" duration={4000} delay={1200} {...strong} />,
},
{
label: 'animated={false} — plain text in shineColor, same box',
content: (
<VariantRow>
<ShinyText text="No sweep at all" animated={false} {...strong} />
</VariantRow>
),
},
{
label: 'in a card, with the brand accent',
content: <UpgradeCard />,
},
];
return <VariantList variants={variants} />;Props
Also accepts testID, forwarded to the root node. It exists for end-to-end tests (Maestro drives the app by the accessibility tree) and has no effect on how the component looks or behaves.
| Name | Type | Default | Description |
|---|---|---|---|
text | string | — | The text to animate. Only a string: the effect needs the characters. |
duration? | number | 2400 | Milliseconds a single sweep takes end to end. |
delay? | number | 600 | Pause between sweeps, in ms. |
spread? | number | 0.28 | Width of the bright band as a fraction of the text, from 0 to 1. Small values give a narrow glint, large ones a soft wave. |
animated? | boolean | true | When false it renders plain text in `shineColor`. |
color? | string | theme.colors.textLight | Resting color of the characters. |
shineColor? | string | theme.colors.primary | Color of the band that travels across. |
align? | 'left' | 'center' | 'right' | left | Horizontal alignment of the line. |
style? | StyleProp<TextStyle> | — | Typography only: `color` and `shineColor` win over any color set here. |