People love to discuss their favorite TV shows, movies, and books on the Internet. My favorite place to do this is on Reddit, but if I'm not careful and don't alert other users to possible spoilers in a post or comment that I've written, then I will be downvoted into oblivion.
Luckily for us, Reddit has a neat markdown feature that allows people to conceal text from other Redditors, but it reveals the secret text once it has been clicked. It's a very simple solution, and I will show you how it's done in React.
Let's start with a very minimal React application. I've created the main
App component and have imported our custom
SpoilerText. As you can see, it wraps around other JSX elements.
Implementing it is very easy. The JSX elements that are wrapped around
SpoilerText are accessed with
props.children. We wrap the child elements in a
<div> that has a CSS class applied to it: either
We keep track of whether it's hidden or not with the
useState hook. Initially this
hidden value is true when the component is rendered for the first time, but as soon as the user clicks on the element, the
reveal function is invoked and it updates the value to false.
Now all you need is a bit of CSS to complete the effect.
To recursively select all child elements that are inside of a class called
.App-hidden, then use the
> * css selector. We set the background-color and text color to black, so that it appears to have been censored and redacted. For a better UI experience, I have added a
cursor: pointer rule so that a user knows the element is clickable when they hover their mouse pointer over the hidden text.
I have also added the
::selection pseudo class, so that a spoiler is not accidentally revealed if the user selects or highlights the text.
.App-notHidden simply unsets all of the CSS properties that were applied in
.App-hidden, and this is all achieved with a simple animation. By using
transition: all 1s, the text and background color slowly fade back to defaults. You can control the speed of this and use decimal values or even milliseconds.
That's it! Pretty easy, right?
This component can be extended and support more features very easily. For example, the default color is black and the default animation speed is 1 second. But you could very easily accept optional props to customize the look and feel of this component