<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SubSlay</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #1a1a1a;
--accent-pink: #ff006e;
--accent-purple: #8338ec;
--accent-blue: #3a86ff;
--accent-green: #06ffa5;
--accent-yellow: #ffbe0b;
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.1);
--shadow-glow: 0 0 40px rgba(255, 0, 110, 0.3);
}
body {
background:
radial-gradient(circle at 20% 20%, var(--accent-pink) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, var(--accent-purple) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, var(--accent-blue) 0%, transparent 50%),
linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
color: var(--text-primary);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
position: relative;
overflow-x: hidden;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 25% 25%, rgba(255, 0, 110, 0.1) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(131, 56, 236, 0.1) 0%, transparent 50%);
pointer-events: none;
z-index: -1;
}
.container {
background: var(--glass-bg);
backdrop-filter: blur(24px);
border: 1px solid var(--glass-border);
border-radius: 32px;
padding: 2.5rem;
max-width: 700px;
width: 100%;
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
}
.container::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.02), transparent);
pointer-events: none;
}
.title {
text-align: center;
font-size: clamp(2.5rem, 5vw, 3.5rem);
font-weight: 700;
margin-bottom: 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
position: relative;
}
.title-text {
background: linear-gradient(
45deg,
var(--accent-pink) 0%,
var(--accent-purple) 25%,
var(--accent-blue) 50%,
var(--accent-green) 75%,
var(--accent-yellow) 100%
);
background-size: 300% 300%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: gradientShift 6s ease-in-out infinite;
filter: drop-shadow(0 0 20px rgba(255, 0, 110, 0.4));
}
.title-emoji {
font-size: clamp(2.5rem, 5vw, 3.5rem);
filter: drop-shadow(0 0 15px rgba(255, 0, 110, 0.6));
animation: bounce 3s ease-in-out infinite;
}
.subtitle {
text-align: center;
font-size: 1.1rem;
color: var(--text-secondary);
margin-bottom: 2rem;
font-weight: 500;
opacity: 0.8;
}
.input-section {
display: grid;
gap: 2rem;
margin-bottom: 2rem;
}
.input-wrapper {
position: relative;
}
.input-label {
font-size: 0.9rem;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 0.75rem;
display: block;
text-transform: uppercase;
letter-spacing: 0.5px;
}
textarea {
width: 100%;
padding: 1.75rem;
border: 2px solid var(--glass-border);
border-radius: 20px;
background: rgba(0, 0, 0, 0.4);
color: var(--text-primary);
font-family: inherit;
font-size: 1.1rem;
min-height: 140px;
resize: vertical;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(12px);
}
textarea:focus {
outline: none;
border-color: var(--accent-pink);
box-shadow:
0 0 40px rgba(255, 0, 110, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
textarea::placeholder {
color: rgba(255, 255, 255, 0.4);
font-style: italic;
}
.output-wrapper {
position: relative;
}
.output {
width: 100%;
padding: 1.75rem;
border: 2px solid var(--glass-border);
border-radius: 20px;
background: rgba(0, 0, 0, 0.4);
color: var(--text-primary);
font-size: 1.2rem;
line-height: 1.7;
min-height: 140px;
word-wrap: break-word;
overflow-wrap: break-word;
position: relative;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(12px);
font-weight: 500;
}
.output.active {
border-color: var(--accent-purple);
box-shadow:
0 0 40px rgba(131, 56, 236, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
.output.empty {
color: rgba(255, 255, 255, 0.4);
font-style: italic;
}
.status {
text-align: center;
padding: 1.25rem 2rem;
border-radius: 16px;
font-weight: 600;
margin-bottom: 2rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
border: 1px solid transparent;
backdrop-filter: blur(12px);
font-size: 1.1rem;
}
.status.loading {
background: linear-gradient(45deg, var(--accent-yellow), var(--accent-green));
color: var(--bg-primary);
animation: statusPulse 2s ease-in-out infinite;
border-color: rgba(255, 255, 255, 0.2);
}
.status.ready {
background: linear-gradient(45deg, var(--accent-green), var(--accent-blue));
color: var(--bg-primary);
box-shadow: 0 0 30px rgba(6, 255, 165, 0.3);
}
.status.error {
background: linear-gradient(45deg, #ff4757, #ff3838);
color: var(--text-primary);
box-shadow: 0 0 30px rgba(255, 71, 87, 0.3);
}
.footer {
text-align: center;
font-size: 0.9rem;
color: var(--text-secondary);
margin-top: 2.5rem;
padding-top: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
font-weight: 500;
}
.footer::before {
content: '✨';
margin-right: 0.5rem;
}
.footer::after {
content: '✨';
margin-left: 0.5rem;
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
@keyframes bounce {
0%, 100% { transform: translateY(0) rotate(0deg); }
25% { transform: translateY(-5px) rotate(5deg); }
50% { transform: translateY(0) rotate(0deg); }
75% { transform: translateY(-3px) rotate(-3deg); }
}
@keyframes statusPulse {
0%, 100% {
opacity: 1;
transform: scale(1);
box-shadow: 0 0 30px rgba(255, 190, 11, 0.3);
}
50% {
opacity: 0.9;
transform: scale(1.02);
box-shadow: 0 0 40px rgba(255, 190, 11, 0.5);
}
}
@media (max-width: 768px) {
.container {
padding: 2rem;
margin: 0.75rem;
border-radius: 24px;
}
.title {
font-size: 2.5rem;
gap: 0.5rem;
}
.subtitle {
font-size: 1rem;
}
textarea, .output {
padding: 1.5rem;
font-size: 1rem;
min-height: 120px;
}
.status {
padding: 1rem 1.5rem;
font-size: 1rem;
}
}
@media (max-width: 480px) {
.container {
padding: 1.5rem;
margin: 0.5rem;
}
.input-section {
gap: 1.5rem;
}
textarea, .output {
padding: 1.25rem;
font-size: 0.95rem;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<div class="container">
<h1 class="title">
<span class="title-text">Sub</span>
<span class="title-emoji">💅🏻</span>
</h1>
<div class="status loading" id="status">Loading awesome sauce...</div>
<div class="input-section">
<div class="input-wrapper">
<textarea
id="input"
placeholder="Type your text here and watch the magic happen... ✨"
disabled
></textarea>
</div>
<div class="output-wrapper">
<div class="output empty" id="output">Your emoji-fied text will appear here...</div>
</div>
</div>
<div class="footer">© 2025 SubSlay — Built to Slay, Not Obey 💅</div>
</div>
<script type="module">
import init, { EmojiStylist } from './pkg/subslay.js';
let transformer = null;
const statusEl = document.getElementById('status');
const inputEl = document.getElementById('input');
const outputEl = document.getElementById('output');
async function initializeApp() {
try {
statusEl.textContent = 'Loading awesome sauce...';
await init('./pkg/subslay_bg.wasm');
const [emojiResponse, gloveResponse] = await Promise.all([
fetch('./static/emoji.json'),
fetch('./static/glove.txt')
]);
if (!emojiResponse.ok || !gloveResponse.ok) {
throw new Error('Failed to load required files');
}
transformer = new EmojiStylist(
await emojiResponse.text(),
await gloveResponse.text()
);
statusEl.textContent = '✅ Ready to slay!';
statusEl.className = 'status ready';
inputEl.disabled = false;
inputEl.focus();
setTimeout(() => {
statusEl.style.transform = 'scale(1.05)';
setTimeout(() => {
statusEl.style.transform = 'scale(1)';
}, 200);
}, 100);
} catch (error) {
statusEl.textContent = '❌ Error: ' + error.message;
statusEl.className = 'status error';
console.error('App initialization failed:', error);
}
}
function transformText() {
const inputText = inputEl.value.trim();
if (!transformer || !inputText) {
outputEl.textContent = 'Your emoji-fied text will appear here...';
outputEl.classList.remove('active');
outputEl.classList.add('empty');
return;
}
try {
const result = transformer.slay(inputText);
outputEl.textContent = result.join(' ');
outputEl.classList.add('active');
outputEl.classList.remove('empty');
outputEl.style.transform = 'translateY(-5px)';
setTimeout(() => {
outputEl.style.transform = 'translateY(-2px)';
}, 200);
} catch (error) {
outputEl.textContent = '❌ Error transforming text';
outputEl.classList.remove('active');
outputEl.classList.add('empty');
console.error('Text transformation failed:', error);
}
}
let transformTimer;
inputEl.addEventListener('input', () => {
clearTimeout(transformTimer);
transformTimer = setTimeout(transformText, 250);
});
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey) {
switch (e.key) {
case 'Enter':
e.preventDefault();
transformText();
break;
case 'r':
e.preventDefault();
inputEl.value = '';
transformText();
inputEl.focus();
break;
}
}
});
initializeApp();
</script>
</body>
</html>