Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active April 1, 2026 23:42
Show Gist options
  • Select an option

  • Save greggman/99780b1a6c7235d083e7a812bb494e40 to your computer and use it in GitHub Desktop.

Select an option

Save greggman/99780b1a6c7235d083e7a812bb494e40 to your computer and use it in GitHub Desktop.
CSS face element
:root {
--face-size: 100px;
--line-color: #333;
--line-width: 4px;
--skin-color: #ffcc00;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
}
/* --- THE SINGLE ELEMENT FACE --- */
face, .face {
display: block;
width: var(--face-size);
height: var(--face-size);
border: var(--line-width) solid var(--line-color);
border-radius: 50%;
background-color: var(--skin-color);
position: relative;
box-sizing: border-box;
transition: all 0.3s ease;
/* Background layers for EYES */
background-repeat: no-repeat;
}
/* 1. EYES (Background Gradients on the main element) */
.eyes-open {
background-image:
radial-gradient(circle, var(--line-color) 50%, transparent 55%),
radial-gradient(circle, var(--line-color) 50%, transparent 55%);
background-size: 16% 16%;
background-position: 30% 55%, 70% 55%;
}
.eyes-closed-up {
background-image:
radial-gradient(ellipse at 50% 100%, transparent 45%, var(--line-color) 46%, var(--line-color) 64%, transparent 65%),
radial-gradient(ellipse at 50% 100%, transparent 45%, var(--line-color) 46%, var(--line-color) 64%, transparent 65%);
background-size: 32% 16%;
background-position: 25% 55%, 75% 55%;
}
.eyes-closed-down {
background-image:
radial-gradient(ellipse at 50% 0%, transparent 45%, var(--line-color) 46%, var(--line-color) 64%, transparent 65%),
radial-gradient(ellipse at 50% 0%, transparent 45%, var(--line-color) 46%, var(--line-color) 64%, transparent 65%);
background-size: 32% 16%;
background-position: 25% 55%, 75% 55%;
}
.eyes-wink {
background-image:
radial-gradient(circle, var(--line-color) 50%, transparent 55%),
linear-gradient(0deg, transparent 40%, var(--line-color) 41%, var(--line-color) 59%, transparent 60%);
background-size: 16% 16%, 16% 16%;
background-position: 30% 55%, 65% 55%;
}
/* 2. EYEBROWS (::before) */
face::before {
content: '';
position: absolute;
top: 15%;
left: 50%;
transform: translateX(-50%);
width: 80%;
height: 30px; /* Taller box allows slant to be visible */
background-repeat: no-repeat;
background-size: 25px 100%;
transition: all 0.3s ease;
}
.brows-up::before {
background-image:
linear-gradient(-25deg, transparent 40%, var(--line-color) 41%, var(--line-color) 56%, transparent 57%),
linear-gradient(25deg, transparent 40%, var(--line-color) 41%, var(--line-color) 56%, transparent 57%);
background-position: 15% 50%, 85% 50%;
}
.brows-down::before {
background-image:
linear-gradient(25deg, transparent 40%, var(--line-color) 41%, var(--line-color) 56%, transparent 57%),
linear-gradient(-25deg, transparent 40%, var(--line-color) 41%, var(--line-color) 56%, transparent 57%);
background-position: 15% 50%, 85% 50%;
}
.brows-none::before { opacity: 0; }
/* 3. MOUTH (::after) */
face::after {
content: '';
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 5%;
width: 80%;
transition: all 0.3s ease;
background-repeat: no-repeat;
}
.smile-closed::after {
height: 80%;
background-image:
radial-gradient(ellipse at 50% 0%, transparent 45%, var(--line-color) 46%, var(--line-color) 64%, transparent 65%);
background-size: 64% 32%;
background-position: 50% 100%;
}
.smile-open::after {
height: 80%;
background-image:
radial-gradient(ellipse at 50% 0%, transparent 55%, var(--line-color) 56%, var(--line-color) 72%, transparent 73%),
linear-gradient(0deg, transparent 79%, var(--line-color) 80%, var(--line-color) 100%);
background-size: 64% 32%;
background-position: 50% 100%;
}
.frown::after {
height: 80%;
background-image:
radial-gradient(ellipse at 50% 100%, transparent 45%, var(--line-color) 46%, var(--line-color) 64%, transparent 65%);
background-size: 64% 32%;
background-position: 50% 90%;
}
.oh::after {
width: 16%;
height: 16%;
bottom: 10%;
border: var(--line-width) solid var(--line-color);
border-radius: 50%;
}
.anger::after {
height: 80%;
background-image:
radial-gradient(ellipse at 50% 100%, transparent 55%, var(--line-color) 56%, var(--line-color) 72%, transparent 73%),
linear-gradient(0deg, var(--line-color) 0%, var(--line-color) 20%, transparent 21%);
background-size: 64% 32%;
background-position: 50% 90%;
}
.hrm::after {
height: 80%;
background-image:
radial-gradient(ellipse at 50% 0%, transparent 45%, var(--line-color) 46%, var(--line-color) 84%, transparent 85%),
radial-gradient(ellipse at 50% 100%, transparent 45%, var(--line-color) 46%, var(--line-color) 84%, transparent 85%),
radial-gradient(ellipse at 50% 0%, transparent 45%, var(--line-color) 46%, var(--line-color) 84%, transparent 85%),
radial-gradient(ellipse at 50% 100%, transparent 45%, var(--line-color) 46%, var(--line-color) 84%, transparent 85%),
radial-gradient(ellipse at 50% 0%, transparent 45%, var(--line-color) 46%, var(--line-color) 84%, transparent 85%);
background-position: 16% 80%, 32% 73%, 48% 80%, 64% 73%, 80% 80%;
background-size: 16% 8%;
}
/* --- DEMO STYLING --- */
.container { text-align: center; }
.controls {
margin-top: 30px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 15px;
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
}
.control-item { display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
label { font-size: 12px; font-weight: bold; color: #888; text-transform: uppercase; }
select { padding: 8px 12px; border-radius: 8px; border: 1px solid #ddd; background: #fff; cursor: pointer; }
.code { margin-top: 25px; font-family: 'Courier New', Courier, monospace; background: #2d2d2d; color: #a6e22e; padding: 15px; border-radius: 8px; font-size: 14px; }
<div class="container">
<face id="targetFace" class="brows-up eyes-open frown"></face>
<div class="code" id="codeOut">
&lt;face class="brows-up eyes-open smile-closed"&gt;&lt;/face&gt;
</div>
<div class="controls">
<div class="control-item">
<label>Eyebrows</label>
<select id="b" onchange="up()">
<option value="brows-none">None</option>
<option value="brows-up" selected>Up (/ \)</option>
<option value="brows-down">Down (\ /)</option>
</select>
</div>
<div class="control-item">
<label>Eyes</label>
<select id="e" onchange="up()">
<option value="eyes-open" Open</option>
<option value="eyes-closed-up">Closed (n n)</option>
<option value="eyes-closed-down">Closed (u u)</option>
<option value="eyes-wink" selected>Wink</option>
</select>
</div>
<div class="control-item">
<label>Mouth</label>
<select id="m" onchange="up()">
<option value="smile-closed" >Smile (U)</option>
<option value="smile-open">Smile (D)</option>
<option value="frown">Frown (n)</option>
<option value="oh">Oh (o)</option>
<option value="anger">Anger (D-up)</option>
<option value="hrm" selected>Hrm (~)</option>
</select>
</div>
</div>
</div>
function up() {
const f = document.getElementById('targetFace');
const b = document.getElementById('b').value;
const e = document.getElementById('e').value;
const m = document.getElementById('m').value;
const classes = `${b} ${e} ${m}`;
f.className = classes;
document.getElementById('codeOut').innerText = `<face class="${classes}"></face>`;
}
up();
{"name":"CSS face element","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment