Skip to content

Commit 9a8ac86

Browse files
Merge pull request #1 from princesatapathy/princesatapathy-patch-1
Add files via upload
2 parents 0cf82f1 + e808564 commit 9a8ac86

File tree

4 files changed

+358
-0
lines changed

4 files changed

+358
-0
lines changed

Javascript/flip a coin/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Flip a Coin Simulator
2+
3+
A web-based coin flip simulator inspired by Google's coin flip feature. Flip a virtual coin with smooth 3D animations and get random heads or tails results.
4+
5+
## Features
6+
7+
- 🎲 **Random coin flips** - Fair 50/50 chance for heads or tails
8+
- 🎨 **Smooth 3D animations** - Beautiful spinning coin effect with CSS 3D transforms
9+
- ⌨️ **Keyboard support** - Press Space or Enter to flip
10+
-**Accessibility** - Supports reduced motion preferences and proper ARIA labels
11+
- 📱 **Responsive design** - Works on desktop and mobile devices
12+
- 🎯 **Simple interface** - Clean, modern UI with dark theme
13+
14+
## How to Use
15+
16+
1. Open `index.html` in any modern web browser
17+
2. Click the "Flip" button or press Space/Enter to flip the coin
18+
3. Watch the coin spin and see the result (Heads or Tails)
19+
20+
## Files
21+
22+
- `index.html` - Main HTML structure
23+
- `style.css` - Styling and animations
24+
- `script.js` - Flip logic and interactions
25+
26+
## Browser Compatibility
27+
28+
Works in all modern browsers that support:
29+
- CSS 3D Transforms
30+
- Web Animations API (with fallback to CSS transitions)
31+
- ES6 JavaScript
32+
33+
## Technical Details
34+
35+
- Uses CSS `transform-style: preserve-3d` for 3D coin effect
36+
- Implements both Web Animations API and CSS transition fallback
37+
- Respects `prefers-reduced-motion` media query for accessibility
38+
- Fair random selection using `Math.random()`
39+
40+
## License
41+
42+
Free to use and modify.
43+

Javascript/flip a coin/index.html

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<title>Flip a coin</title>
8+
<meta name="description" content="Flip a coin simulator like Google">
9+
<link rel="preconnect" href="https://fonts.googleapis.com">
10+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
12+
<link rel="stylesheet" href="style.css">
13+
</head>
14+
15+
<body>
16+
<main class="app" role="main">
17+
<h1 class="title">Flip a coin</h1>
18+
19+
<section class="stage" aria-label="Coin flip area">
20+
<div id="coin" class="coin" aria-hidden="true">
21+
<div class="face heads" data-label="Heads">Heads</div>
22+
<div class="face tails" data-label="Tails">Tails</div>
23+
</div>
24+
</section>
25+
26+
<div class="controls">
27+
<button id="flipBtn" class="btn" type="button" aria-label="Flip the coin">Flip</button>
28+
</div>
29+
30+
<p id="result" class="result" aria-live="polite" aria-atomic="true">Tap Flip to start</p>
31+
</main>
32+
33+
<script src="script.js"></script>
34+
35+
</body>
36+
37+
</html>

Javascript/flip a coin/script.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
(() => {
2+
const coin = document.getElementById('coin');
3+
const flipBtn = document.getElementById('flipBtn');
4+
const resultEl = document.getElementById('result');
5+
6+
let isFlipping = false;
7+
let currentAngle = 0; // 0 => heads shown; 180 => tails shown (mod 360)
8+
9+
function getTargetAngle(face) {
10+
return face === 'heads' ? 0 : 180;
11+
}
12+
13+
function chooseRandomFace() {
14+
return Math.random() < 0.5 ? 'heads' : 'tails';
15+
}
16+
17+
function setImmediateFace(face) {
18+
currentAngle = getTargetAngle(face);
19+
coin.style.transform = `rotateY(${currentAngle}deg)`;
20+
coin.classList.toggle('show-heads', face === 'heads');
21+
coin.classList.toggle('show-tails', face === 'tails');
22+
coin.classList.remove('spinning');
23+
}
24+
25+
function announce(face) {
26+
resultEl.textContent = face === 'heads' ? 'Heads' : 'Tails';
27+
}
28+
29+
function flip(targetFace) {
30+
if (isFlipping) return;
31+
isFlipping = true;
32+
flipBtn.disabled = true;
33+
34+
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
35+
const target = targetFace || chooseRandomFace();
36+
37+
if (prefersReduced) {
38+
setImmediateFace(target);
39+
announce(target);
40+
isFlipping = false;
41+
flipBtn.disabled = false;
42+
return;
43+
}
44+
45+
const targetAngle = getTargetAngle(target);
46+
// Add multiple full spins for flair
47+
const fullSpins = 6; // even number so facing side logic is preserved cleanly
48+
const start = currentAngle;
49+
const end = start + fullSpins * 180 + (targetAngle - (start % 360));
50+
51+
const duration = 900; // ms
52+
const supportsWAAPI = typeof coin.animate === 'function';
53+
54+
// During the spin, allow both faces to be visible to avoid popping
55+
coin.classList.remove('show-heads', 'show-tails');
56+
coin.classList.add('spinning');
57+
58+
if (supportsWAAPI) {
59+
coin.animate(
60+
[
61+
{ transform: `rotateY(${start}deg)` },
62+
{ transform: `rotateY(${end}deg)` }
63+
],
64+
{
65+
duration,
66+
easing: 'cubic-bezier(.22,.61,.36,1)',
67+
fill: 'forwards'
68+
}
69+
).addEventListener('finish', () => {
70+
currentAngle = ((end % 360) + 360) % 360;
71+
// Snap to exact final state to avoid subpixel drift
72+
coin.style.transform = `rotateY(${currentAngle}deg)`;
73+
coin.classList.toggle('show-heads', target === 'heads');
74+
coin.classList.toggle('show-tails', target === 'tails');
75+
coin.classList.remove('spinning');
76+
announce(target);
77+
isFlipping = false;
78+
flipBtn.disabled = false;
79+
});
80+
} else {
81+
// Fallback: use CSS transition defined on .coin
82+
// Set starting transform explicitly to ensure transition runs from the right place
83+
coin.style.transform = `rotateY(${start}deg)`;
84+
// Next frame, set to end to trigger transition
85+
requestAnimationFrame(() => {
86+
coin.style.transform = `rotateY(${end}deg)`;
87+
});
88+
window.setTimeout(() => {
89+
currentAngle = ((end % 360) + 360) % 360;
90+
coin.style.transform = `rotateY(${currentAngle}deg)`;
91+
coin.classList.toggle('show-heads', target === 'heads');
92+
coin.classList.toggle('show-tails', target === 'tails');
93+
coin.classList.remove('spinning');
94+
announce(target);
95+
isFlipping = false;
96+
flipBtn.disabled = false;
97+
}, duration);
98+
}
99+
}
100+
101+
flipBtn.addEventListener('click', () => flip());
102+
103+
// Keyboard: Space/Enter to flip
104+
document.addEventListener('keydown', (e) => {
105+
if (e.key === ' ' || e.key === 'Enter') {
106+
e.preventDefault();
107+
flip();
108+
}
109+
});
110+
111+
// Initialize with a fair random face so UI doesn’t feel static
112+
setImmediateFace(chooseRandomFace());
113+
})();
114+
115+

Javascript/flip a coin/style.css

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
:root {
2+
--bg: #0b0b10;
3+
--card: #12121a;
4+
--text: #e9eaf0;
5+
--muted: #a8adbd;
6+
--accent: #6750a4;
7+
/* Googley purple */
8+
--accent-2: #7c4dff;
9+
--shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
10+
}
11+
12+
* {
13+
box-sizing: border-box;
14+
}
15+
16+
html,
17+
body {
18+
height: 100%;
19+
}
20+
21+
body {
22+
margin: 0;
23+
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
24+
background: radial-gradient(1200px 800px at 50% -10%, #1a1b27 0%, var(--bg) 55%);
25+
color: var(--text);
26+
}
27+
28+
.app {
29+
min-height: 100vh;
30+
display: grid;
31+
grid-template-rows: auto 1fr auto auto;
32+
place-items: center;
33+
gap: 24px;
34+
padding: 40px 16px;
35+
}
36+
37+
.title {
38+
margin: 0;
39+
font-size: clamp(24px, 4vw, 36px);
40+
font-weight: 800;
41+
letter-spacing: -0.02em;
42+
}
43+
44+
.stage {
45+
width: min(520px, 92vw);
46+
height: min(520px, 92vw);
47+
display: grid;
48+
place-items: center;
49+
perspective: 1200px;
50+
}
51+
52+
.coin {
53+
width: 52%;
54+
aspect-ratio: 1/1;
55+
position: relative;
56+
transform-style: preserve-3d;
57+
transform: rotateY(0deg);
58+
transition: transform 0.8s cubic-bezier(.22, .61, .36, 1);
59+
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, .45));
60+
}
61+
62+
.face {
63+
position: absolute;
64+
inset: 0;
65+
display: grid;
66+
place-items: center;
67+
border-radius: 50%;
68+
font-weight: 800;
69+
text-transform: uppercase;
70+
letter-spacing: 0.08em;
71+
visibility: visible;
72+
opacity: 0;
73+
transition: opacity .18s ease;
74+
}
75+
76+
.face.heads {
77+
background: radial-gradient(120% 120% at 30% 30%, #ffd27a 0%, #f2b741 40%, #d6961f 70%, #ae6d00 100%);
78+
color: #3a2610;
79+
border: 6px solid rgba(255, 219, 120, 0.65);
80+
transform: rotateY(0deg);
81+
}
82+
83+
.face.tails {
84+
background: radial-gradient(120% 120% at 30% 30%, #c8d7ff 0%, #9db8ff 40%, #7896ff 70%, #4a6bff 100%);
85+
color: #0f1a4a;
86+
border: 6px solid rgba(180, 198, 255, 0.7);
87+
transform: rotateY(180deg);
88+
}
89+
90+
/* Show correct face at rest */
91+
.coin.show-heads .face.heads {
92+
opacity: 1;
93+
}
94+
95+
.coin.show-heads .face.tails {
96+
opacity: 0;
97+
}
98+
99+
.coin.show-tails .face.tails {
100+
opacity: 1;
101+
}
102+
103+
.coin.show-tails .face.heads {
104+
opacity: 0;
105+
}
106+
107+
/* During spin show both to avoid popping */
108+
.coin.spinning .face {
109+
opacity: 1;
110+
}
111+
112+
/* Subtle rim */
113+
.coin::before {
114+
content: "";
115+
position: absolute;
116+
inset: -2%;
117+
border-radius: 50%;
118+
background: conic-gradient(from 0deg, #0000 0 20%, rgba(0, 0, 0, .35) 20% 30%, #0000 30% 70%, rgba(255, 255, 255, .25) 70% 80%, #0000 80% 100%);
119+
filter: blur(1px);
120+
z-index: -1;
121+
}
122+
123+
.controls {
124+
display: grid;
125+
place-items: center;
126+
gap: 12px;
127+
}
128+
129+
.btn {
130+
appearance: none;
131+
border: 0;
132+
padding: 14px 28px;
133+
font-size: 16px;
134+
font-weight: 700;
135+
color: white;
136+
background: linear-gradient(135deg, var(--accent), var(--accent-2));
137+
border-radius: 999px;
138+
box-shadow: var(--shadow);
139+
cursor: pointer;
140+
}
141+
142+
.btn:disabled {
143+
opacity: .6;
144+
cursor: not-allowed;
145+
}
146+
147+
.btn:focus-visible {
148+
outline: 3px solid rgba(124, 77, 255, .6);
149+
outline-offset: 3px;
150+
}
151+
152+
.result {
153+
margin: 0;
154+
font-size: clamp(18px, 2.6vw, 22px);
155+
color: var(--muted);
156+
}
157+
158+
/* Reduced motion */
159+
@media (prefers-reduced-motion: reduce) {
160+
.coin {
161+
transition: none;
162+
}
163+
}

0 commit comments

Comments
 (0)