Creating a 3D Calculator in React.js using Vite
In this step-by-step guide, I’ll walk you through creating a 3D calculator using React.js with Vite as the build tool. We’ll use CSS for the 3D effects and basic calculator functionality.
Step 1: Set up the project with Vite
Open your terminal and run:
npm create vite@latest 3d-calculator --template react
cd 3d-calculator
npm install
2. Install additional dependencies we’ll need:
npm install styled-components
Step 2: Clean up the template
Delete unnecessary files and clean up src/App.jsx
to start fresh.
Step 3: Create the Calculator component
Create a new file src/components/Calculator.jsx
:
import { useState } from 'react';
import styled from 'styled-components';
const CalculatorContainer = styled.div`
width: 320px;
height: 500px;
perspective: 1000px;
margin: 50px auto;
`;
const CalculatorBody = styled.div`
width: 100%;
height: 100%;
position: relative;
// transform-style: preserve-3d;
// transform: rotateX(10deg) rotateY(-10deg);
// transition: transform 0.5s;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
&:hover {
//transform: rotateX(10deg) rotateY(-10deg) translateY(-10px);
}
`;
const CalculatorFace = styled.div`
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #2c3e50, #4ca1af);
border-radius: 15px;
display: flex;
flex-direction: column;
padding: 20px;
box-sizing: border-box;
backface-visibility: hidden;
border: 1px solid rgba(255, 255, 255, 0.1);
`;
const Display = styled.div`
background: rgba(0, 0, 0, 0.3);
color: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
text-align: right;
font-size: 2rem;
height: 80px;
display: flex;
flex-direction: column;
justify-content: space-between;
`;
const PreviousOperand = styled.div`
font-size: 1rem;
opacity: 0.7;
height: 20px;
`;
const CurrentOperand = styled.div`
font-size: 2rem;
word-wrap: break-word;
word-break: break-all;
`;
const ButtonGrid = styled.div`
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 10px;
flex-grow: 1;
`;
const Button = styled.button`
border: none;
border-radius: 10px;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 1.5rem;
cursor: pointer;
transition: all 0.2s;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.1);
&:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
&:active {
transform: translateY(0);
}
&.span-two {
grid-column: span 2;
}
&.operation {
background: rgba(255, 165, 0, 0.4);
}
&.function {
background: rgba(70, 130, 180, 0.4);
}
`;
const CalculatorSide = styled.div`
position: absolute;
width: 20px;
height: 100%;
background: linear-gradient(to right, #1a2a3a, #3a5a6a);
transform-origin: left;
transform: rotateY(90deg);
left: 0;
border-radius: 15px 0 0 15px;
`;
const CalculatorTop = styled.div`
position: absolute;
width: 100%;
height: 20px;
background: linear-gradient(to bottom, #3a5a6a, #4a6a7a);
transform-origin: top;
transform: rotateX(-90deg);
top: 0;
border-radius: 15px 15px 0 0;
`;
export default function Calculator() {
const [currentOperand, setCurrentOperand] = useState('0');
const [previousOperand, setPreviousOperand] = useState('');
const [operation, setOperation] = useState(null);
const [overwrite, setOverwrite] = useState(true);
const clear = () => {
setCurrentOperand('0');
setPreviousOperand('');
setOperation(null);
setOverwrite(true);
};
const deleteNumber = () => {
if (currentOperand.length === 1 || (currentOperand.length === 2 && currentOperand.startsWith('-'))) {
setCurrentOperand('0');
setOverwrite(true);
} else {
setCurrentOperand(currentOperand.slice(0, -1));
}
};
const addDigit = (digit) => {
if (currentOperand === '0' || overwrite) {
setCurrentOperand(digit);
setOverwrite(false);
} else {
setCurrentOperand(`${currentOperand}${digit}`);
}
};
const chooseOperation = (op) => {
if (currentOperand === '0' && previousOperand === '') return;
if (previousOperand === '') {
setPreviousOperand(currentOperand);
setCurrentOperand('0');
setOperation(op);
setOverwrite(true);
} else {
const result = calculate();
setPreviousOperand(result);
setCurrentOperand('0');
setOperation(op);
setOverwrite(true);
}
};
const calculate = () => {
if (!operation || previousOperand === '' || currentOperand === '0') return currentOperand;
const prev = parseFloat(previousOperand);
const current = parseFloat(currentOperand);
let result;
switch (operation) {
case '+':
result = prev + current;
break;
case '-':
result = prev - current;
break;
case '*':
result = prev * current;
break;
case '÷':
result = prev / current;
break;
default:
return;
}
return result.toString();
};
const equals = () => {
if (previousOperand === '' || currentOperand === '0' || !operation) return;
const result = calculate();
setCurrentOperand(result);
setPreviousOperand('');
setOperation(null);
setOverwrite(true);
};
const addDecimal = () => {
if (overwrite) {
setCurrentOperand('0.');
setOverwrite(false);
return;
}
if (currentOperand.includes('.')) return;
setCurrentOperand(`${currentOperand}.`);
};
const toggleSign = () => {
if (currentOperand === '0') return;
if (currentOperand.startsWith('-')) {
setCurrentOperand(currentOperand.slice(1));
} else {
setCurrentOperand(`-${currentOperand}`);
}
};
const percentage = () => {
const value = parseFloat(currentOperand);
setCurrentOperand((value / 100).toString());
};
return (
<CalculatorContainer>
<CalculatorBody>
<CalculatorFace>
<Display>
<PreviousOperand>
{previousOperand} {operation}
</PreviousOperand>
<CurrentOperand>{currentOperand}</CurrentOperand>
</Display>
<ButtonGrid>
<Button className="function" onClick={clear}>
AC
</Button>
<Button className="function" onClick={toggleSign}>
+/-
</Button>
<Button className="function" onClick={percentage}>
%
</Button>
<Button className="operation" onClick={() => chooseOperation('÷')}>
÷
</Button>
<Button onClick={() => addDigit('7')}>7</Button>
<Button onClick={() => addDigit('8')}>8</Button>
<Button onClick={() => addDigit('9')}>9</Button>
<Button className="operation" onClick={() => chooseOperation('*')}>
×
</Button>
<Button onClick={() => addDigit('4')}>4</Button>
<Button onClick={() => addDigit('5')}>5</Button>
<Button onClick={() => addDigit('6')}>6</Button>
<Button className="operation" onClick={() => chooseOperation('-')}>
-
</Button>
<Button onClick={() => addDigit('1')}>1</Button>
<Button onClick={() => addDigit('2')}>2</Button>
<Button onClick={() => addDigit('3')}>3</Button>
<Button className="operation" onClick={() => chooseOperation('+')}>
+
</Button>
<Button className="span-two" onClick={() => addDigit('0')}>
0
</Button>
<Button onClick={addDecimal}>.</Button>
<Button className="operation" onClick={equals}>
=
</Button>
</ButtonGrid>
</CalculatorFace>
<CalculatorSide />
<CalculatorTop />
</CalculatorBody>
</CalculatorContainer>
);
}
Step 4: Update App.jsx
Modify src/App.jsx
to use our new Calculator component:
import Calculator from './components/Calculator';
import styled from 'styled-components';
const AppContainer = styled.div`
background: linear-gradient(to right, #0f2027, #203a43, #2c5364);
min-height: 100vh;
padding: 20px;
`;
function App() {
return (
<AppContainer>
<Calculator />
</AppContainer>
);
}
export default App;
Step 5: Run the application
Start the development server:
npm run dev
Your 3D calculator should now be running at http://localhost:5173
(or similar port).
Features of this 3D Calculator:
- Real 3D Perspective: Uses CSS 3D transforms to create a realistic 3D effect
- Full Calculator Functionality:
- Basic operations (+, -, *, /)
- Clear and delete functions
- Decimal point support
- Percentage calculations
- Positive/negative toggle
- Visual Effects:
- Subtle hover animations
- Button press animations
- Glass-morphism style display
- Responsive Design: Works on different screen sizes
You can further enhance this by:
- Adding more advanced mathematical functions
- Implementing a history feature
- Adding theme switching
- Improving the 3D effect with more sides or shadows
Would you like me to explain any part in more detail or add any specific features to the calculator?
Source Code Download Click Below Button