<div id="root"></div>
html, body {
    min-height: 100%;
    overflow: hidden;
    font-family: 'Helvetica Neue', Helvetica, sans-serif;
}

main {
    padding: 0 2rem;
    position: relative;
    background-color: #eeeeee;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: radial-gradient(ellipse at 0% 100%, rgba(#ec38bc, 0.6), rgba(#ec38bc, 0.1) 75%),
                radial-gradient(ellipse at 100% 0%, rgba(#1CD8D2, 0.7), rgba(#7303c0, 0.1) 75%),
                linear-gradient(#0b8793, #0099F7);
}

.input {
    position: absolute;
    z-index: 1;
    padding: 0.8rem 1rem;
    top: 2rem;
    left: 50%;
    margin-left: -15%;
    width: 30%;
    font-size: 18px;
    font-weight: 100;
    color: #333;
    border: none;
    outline: none;
    border-radius: 3px;
    transition: color 0.3s, background 0.3s;
    background: linear-gradient(90deg, rgba(white, 0.15), rgba(white, 0.1));
    
    &::placeholder {
        color: rgba(white, 0.6);
    }
    
    &:focus {
        color: white;
        background: linear-gradient(90deg, rgba(black, 0.25), rgba(black, 0.2));
        
        &::placeholder {
            color: rgba(black, 0.6);
        }
    }
    
    
}

$time: 0.9s;
$ease: cubic-bezier(0.23, 1, 0.32, 1);

.title {
    font-family: 'futura-pt', serif;
    font-weight: 400;
    font-size: 14vmin;
    text-rendering: optimizeLegibility;
    color: white;
    line-height: 1.15;
    transition: opacity $time $ease;
    opacity: 0;

    > span {
        position: relative;
        padding: 0 0.05em; // prevents some clipping issues due to the font bounds
        display: inline-block;
        margin-right: 0.15em;
        margin-top: -0.2em;
        margin-bottom: -0.2em;
        overflow: hidden;

        span {
            display: block;
            transition: transform $time $ease;
            transform: translateY(100%);
        }
    }
}

.animate {
    opacity: 1;
    > span {
        span {
            transform: translateY(0%);
        }
    }
}
View Compiled
/**
 * -------------------------------------------------------
 * Word by Word Unmasking Animation
 * -------------------------------------------------------
 *
 * Text animation effect to slide up and unmask each word.
 * Type in the input to see new copy and animation.
 *
 */

function spanifyWithDelay(string) {
    // so this just adds a bunch of spans with a
    // delay necessary for the animation effect.
    
    const delay = 0.04;

    return string
        .trim()
        .split(' ')
        .map((word, idx) => (
            `<span>
                <span style="transition-delay: ${idx * delay}s">
                    ${word}
                </span>
            </span>`
        ))
        .reduce((acc, val) => (acc + val), '');
}
    
class Title extends React.Component {
    state = {
        animate: false,
    };
    
    componentDidMount() {
        // this just add the class to trigger the animation
        // ideally this would be in a transition group
        // or use some other way to trigger the class.
        
        setTimeout(() => {
            this.setState(() => ({
                animate: true,
            }));
        }, 500)
    }  
        
    render() {
        const { title } = this.props;
        const { animate } = this.state;
        
        // NOTE: Class "animate" triggers the animation
        // when the "animate" state it true.
        return (
            <h1
                className={classNames('title', { animate })}
                dangerouslySetInnerHTML={{ __html: spanifyWithDelay(title) }}
            />
        );
    }
}
    
class Input extends React.Component {
    render() {
        return (
            <input
                className="input"
                type="text"
                onChange={this.props.onChange}
                placeholder="Type Here"
            />
        );
    }    
}
    
class App extends React.Component {
    state = {
        value: 'Effect For Word By Word Unmasking Animation',
    };
        
    handleChange = ({currentTarget}) => {
        this.setState({
            value: currentTarget.value,
        });
    };
    
    render() {
        return (
            <main>
                <Input onChange={this.handleChange} />
                <Title key={this.state.value} title={this.state.value} />
            </main>
        );
    }
}

const run = () => {
    const root = document.getElementById('root');
    
    ReactDOM.render(
        <App />,
        root
    );
}

run();

/*!
  Copyright (c) 2017 Jed Watson.
  Licensed under the MIT License (MIT), see
  http://jedwatson.github.io/classnames
*/

function classNames () {
    var classes = [];

    for (var i = 0; i < arguments.length; i++) {
        var arg = arguments[i];
        if (!arg) continue;

        var argType = typeof arg;

        if (argType === 'string' || argType === 'number') {
            classes.push(arg);
        } else if (Array.isArray(arg) && arg.length) {
            var inner = classNames.apply(null, arg);
            if (inner) {
                classes.push(inner);
            }
        } else if (argType === 'object') {
            for (var key in arg) {
                if (hasOwnProperty.call(arg, key) && arg[key]) {
                    classes.push(key);
                }
            }
        }
    }

    return classes.join(' ');
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.development.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.development.js
  3. https://unpkg.com/prop-types@15.5.10/prop-types.js