In this article, we are going to learn how to create a translucent text input in ReactJS.
Prerequisites:
- Knowledge of JavaScript (ES6)
- Knowledge of HTML/CSS.
- Basic knowledge of ReactJS.
React hooks used in building this application are:
JavaScript modules:
Creating React Application and Installing Modules:
Step 1: Now, you will start a new project using create-react-app so open your terminal and type:
npx create-react-app translucent-input-box
Step 2: After creating your project folder i.e. translucent-input-box , move to it using the following command.
cd translucent-input-box
Step 3: Add the npm packages you will need during the project:
npm install framer-motion styled-components
Step 5: Now open your newly created project and open the src folder and delete the following files (Optional):Ā
- logo.svg
- serviceWorker.js
- setupTests.js
- index.css
- App.test.js (if any)
Create a folder named Input and create the following files:
- Component.jsx
- Component.motion.js
- Component.styles.js
Project structure: It will look like this.
Project structure
Approach:Ā
- We are going to create a translucent animated text input using framer-motion and styled components.
- Wrapper, Input, Label, Underline are the styled components used to make the text input box collectively in Component.jsx file.
- In Component.jsx file, we use framer-motion with custom animation variants from the Component.motion.js file to animate the text input box.
- React useState hook is used to manage the state of value that is used as a placeholder attribute & also to set it as a label when active.
- Framer-motion useCycle hook is similar to react useState hook. It cycles through a series of visual properties used for animation. It is used to toggle between or cycle through animation variants.
Implementation:
Filename: App.js
javascript
import React, { useState } from "react"; import "./App.css"; Ā Ā import Input from "./Input"; Ā Ā const App = () => { Ā Ā // The useState hook is used to manage the state ofĀ Ā Ā // "value" that is used as placeholder attribute Ā Ā // and also to set it as a label when clicked Ā Ā const [value, setValue] = useState(""); Ā Ā Ā Ā Ā Ā return ( Ā Ā Ā Ā <div className="App"> Ā Ā Ā Ā Ā Ā <div className="container"> Ā Ā Ā Ā Ā Ā Ā {/* "Input" component created using styled-components Ā Ā Ā Ā Ā Ā Ā and animated using framer-motion Ā Ā Ā Ā Ā Ā Ā */} Ā Ā Ā Ā Ā Ā Ā Ā <Input Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā value={value} Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā onChange={(id, value) => setValue(value)} Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā label={"First name"} Ā Ā Ā Ā Ā Ā Ā Ā /> Ā Ā Ā Ā Ā Ā </div> Ā Ā Ā Ā </div> Ā Ā ); }; Ā Ā export default App; |
Filename: index.js
javascript
import React from "react"; import ReactDOM from "react-dom"; Ā Ā import App from "./App"; Ā Ā const rootElement = document.getElementById("root"); ReactDOM.render( Ā Ā <React.StrictMode> Ā Ā Ā Ā <App /> Ā Ā </React.StrictMode>, Ā Ā rootElement ); |
Filename: App.css
css
.App { Ā Ā font-family: "Times New Roman", Times, serif; Ā Ā text-align: center; Ā Ā Ā Ā width: auto; Ā Ā height: 98vh; Ā Ā Ā Ā display: flex; Ā Ā justify-content: center; Ā Ā align-items: center; Ā Ā Ā Ā overflow: hidden; Ā Ā background: #1e9600; /* fallback for old browsers */Ā Ā background: -webkit-linear-gradient( Ā Ā Ā Ā to right, Ā Ā Ā Ā #ff0000, Ā Ā Ā Ā #fff200, Ā Ā Ā Ā #1e9600Ā Ā ); /* Chrome 10-25, Safari 5.1-6 */Ā Ā background: linear-gradient( Ā Ā Ā Ā to right, Ā Ā Ā Ā #ff0000, Ā Ā Ā Ā #fff200, Ā Ā Ā Ā #1e9600Ā Ā ); } Ā Ā .container { Ā Ā border-radius: 25px; Ā Ā Ā Ā width: 50vw; Ā Ā height: 20vh; Ā Ā Ā Ā display: flex; Ā Ā justify-content: center; Ā Ā align-items: center; Ā Ā Ā Ā opacity: 0.5; Ā Ā background-color: #f1f1f1; } Ā Ā Input { Ā Ā text-decoration: none; Ā Ā background-color: #f1f1f1; Ā Ā width: 40%; } |
Filename: Component.jsx
javascript
import React from "react"; import { Wrapper, Input, Label, Underline } from "./Component.styles"; import { motionLabel, motionUnderline } from "./Component.motion"; import { useCycle } from "framer-motion"; Ā Ā export default ({ label, value, onChange, id, errors }) => { Ā Ā const onTapStart = (event, info) => { Ā Ā Ā Ā focus === "inactive" && cycleFocus(); Ā Ā Ā Ā return blur === "inactive" && cycleBlur(); Ā Ā }; Ā Ā const onBlur = event => { Ā Ā Ā Ā value === "" && cycleFocus(); Ā Ā Ā Ā cycleBlur(); Ā Ā }; Ā Ā const [focus, cycleFocus] = useCycle("inactive", "active"); Ā Ā const [blur, cycleBlur] = useCycle("inactive", "active"); Ā Ā return ( Ā Ā Ā {/* Wrapper,Label,Underline - custom styled-components withĀ Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā some of its attributes Ā Ā Ā */} Ā Ā Ā {/* These all collectively make the animated input box whichĀ Ā Ā Ā then given transluscent background using CSS Ā Ā Ā */}Ā Ā Ā Ā Ā <Wrapper> Ā Ā Ā Ā Ā Ā <Input Ā Ā Ā Ā Ā Ā Ā Ā onTap={onTapStart} Ā Ā Ā Ā Ā Ā Ā Ā placeholder={label} Ā Ā Ā Ā Ā Ā Ā Ā onBlur={e => onBlur(id)} Ā Ā Ā Ā Ā Ā Ā Ā onChange={e => onChange(id, e.target.value)} Ā Ā Ā Ā Ā Ā Ā Ā type={"text"} Ā Ā Ā Ā Ā Ā Ā Ā required Ā Ā Ā Ā Ā Ā Ā Ā value={value} Ā Ā Ā Ā Ā Ā /> Ā Ā Ā Ā Ā Ā <Label {...motionLabel(focus)}>{label}</Label> Ā Ā Ā Ā Ā Ā <Underline {...motionUnderline(blur)} /> Ā Ā Ā Ā </Wrapper> Ā Ā ); |
Filename: Component.motion.js
javascript
const variantsWrapper = { Ā Ā initial: {}, Ā Ā in: {}, Ā Ā out: {}, Ā Ā hover: {}, Ā Ā tap: {} }; Ā Ā const variantsLabel = { Ā Ā active: { Ā Ā Ā Ā x: -15, Ā Ā Ā Ā y: -20, Ā Ā Ā Ā scale: 0.7 Ā Ā }, Ā Ā inactive: { x: 0, y: 0, scale: 1 } }; const variantsUnderline = { Ā Ā active: { Ā Ā Ā Ā width: "100%", Ā Ā Ā Ā transition: { Ā Ā Ā Ā Ā Ā ease: "easeIn", Ā Ā Ā Ā Ā Ā duration: 0.2 Ā Ā Ā Ā } Ā Ā }, Ā Ā inactive: { Ā Ā Ā Ā width: "0", Ā Ā Ā Ā transition: { Ā Ā Ā Ā Ā Ā ease: "easeIn", Ā Ā Ā Ā Ā Ā duration: 0.1 Ā Ā Ā Ā } Ā Ā } }; Ā Ā export const motionLabel = state => { Ā Ā return { Ā Ā Ā Ā animate: state, Ā Ā Ā Ā variants: variantsLabel Ā Ā }; }; Ā Ā export const motionUnderline = state => { Ā Ā return { Ā Ā Ā Ā animate: state, Ā Ā Ā Ā variants: variantsUnderline Ā Ā }; }; Ā Ā export const animationWrapper = { Ā Ā initial: "initial", Ā Ā animate: "in", Ā Ā exit: "out", Ā Ā whileHover: "hover", Ā Ā whileTap: "tap", Ā Ā variants: variantsWrapper }; |
Filename: Component.styles.js
javascript
import styled from "styled-components"; import { motion } from "framer-motion"; Ā Ā // Below are the styled-components used toĀ // make the animated text input box Ā Ā export const Wrapper = styled(motion.div)` Ā Ā position: relative; Ā Ā width: 80%; Ā Ā padding: 18px; Ā Ā padding-bottom: 30px; Ā Ā border-bottom: 1px solid #2f528f; `; Ā Ā export const Label = styled(motion.span)` Ā Ā align-self: center; Ā Ā position: absolute; Ā Ā left: 0; Ā Ā top: 50%; Ā Ā grid-area: input; Ā Ā font-family: Montserrat; Ā Ā font-size: 18px; Ā Ā line-height: 18px; Ā Ā text-align: left; Ā Ā pointer-events: none; Ā Ā font-weight: normal; Ā Ā /* background: green; */`; Ā Ā export const Input = styled(motion.input)` Ā Ā height: 18px; Ā Ā font-size: 18px; Ā Ā -webkit-appearance: none; Ā Ā background: transparent !important; Ā Ā position: absolute; Ā Ā left: 0; Ā Ā top: 50%; Ā Ā padding: 0; Ā Ā padding-bottom: 5px; Ā Ā margin: 0; Ā Ā color: black; Ā Ā border: none; Ā Ā box-shadow: none !important; Ā Ā font-weight: normal; Ā Ā &:focus { Ā Ā Ā Ā outline: none; Ā Ā } Ā Ā &::placeholder { Ā Ā Ā Ā color: #f1f1f1; Ā Ā } `; Ā Ā export const Underline = styled(motion.div)` Ā Ā position: absolute; Ā Ā background-color: #2f528f; Ā Ā bottom: 0; Ā Ā left: 0; Ā Ā width: 100%; Ā Ā height: 3px; `; |
Step to Run Application: Run the application using the following command from the root directory of the project :
npm start
Output: Now open your browser and go to http://localhost:3000/, you will see the following output:
