Gel Button
Glossy polycarbonate button with Fresnel catch-light reflection
Size
Material
State
<!-- Basic -->
<button class="gel-btn">GEL</button>
<!-- Color variants -->
<button class="gel-btn blue">BLUE</button>
<button class="gel-btn green">GREEN</button>
<button class="gel-btn pink">PINK</button>
<!-- Selected -->
<button class="gel-btn active">SELECTED</button>
<!-- Sizes -->
<button class="gel-btn xs">XS</button>
<button class="gel-btn lg">LARGE</button> .gel-btn {
display: inline-flex; align-items: center; justify-content: center;
flex-direction: column; gap: 2px;
font-family: var(--font-ui); font-size: 11px; font-weight: 500;
letter-spacing: 1px; color: var(--text-primary);
background: linear-gradient(180deg, var(--clear-glass), transparent 50%),
linear-gradient(180deg, var(--bg-surface), var(--bg-panel));
border: none; border-radius: var(--radius-md); cursor: pointer;
position: relative; outline: none; user-select: none;
height: 38px; min-width: 64px; padding: 0 16px;
box-shadow: 0 3px 0 var(--border-deep), inset 0 1px 0 var(--glossy-hi);
transition: transform 0.07s var(--snap-fast), box-shadow 0.07s var(--snap-fast);
}
.gel-btn:active {
transform: translateY(2px);
box-shadow: 0 1px 0 var(--border-deep), inset 0 1px 0 transparent;
}
.gel-btn.active { color: var(--blue-info); }
.gel-btn.active::after {
content: ''; position: absolute; bottom: 5px;
left: 50%; transform: translateX(-50%);
width: 18px; height: 2px; background: var(--blue-info); border-radius: 1px;
}
.gel-btn.blue { color: var(--blue-info); }
.gel-btn.green { color: var(--green-on); }
.gel-btn.pink { color: var(--pink-action); }
.gel-btn[disabled] { opacity: 0.4; pointer-events: none; } API
| Class | Type | Description |
|---|---|---|
.gel-btn | Base | Primary component class |
.xs | Size | Extra small variant |
.sm | Size | Small variant |
.md | Size | Medium (default) variant |
.lg | Size | Large variant |
.active | State | Active state |
Design Notes
Physical Analog
Reference devices: iPod Nano 3rd gen click buttons, Sony Ericsson W800i keys, PSP face buttons. Mechanism: Dome-switch mechanism with transparent/translucent polycarbonate (Lexan) keycap, injection-molded with high-gloss finish. 2-3mm thick, slightly convex. Strong specular highlight from Fresnel reflection at air-plastic interface.
Geometry
| Property | Value |
|---|---|
| Keycap material | Transparent/translucent polycarbonate |
| Surface | High-gloss, slightly convex |
| Catch-light | Concentrated bright reflection on upper third, fading to transparent |
| Thickness | 2-3mm polycarbonate |
| Shadow depth | 3px (taller than push-btn's 2px) |
CSS Recipe
Default State
.gel-btn {
display: inline-flex; align-items: center; justify-content: center;
flex-direction: column; gap: 2px;
font-family: var(--font-ui); font-size: 11px; font-weight: 500;
letter-spacing: 1px; color: var(--text-primary);
background: linear-gradient(180deg, var(--clear-glass), transparent 50%),
linear-gradient(180deg, var(--bg-surface), var(--bg-panel));
border: none; border-radius: var(--radius-md); cursor: pointer;
position: relative; outline: none; user-select: none;
height: 38px; min-width: 64px; padding: 0 16px;
box-shadow: 0 3px 0 var(--border-deep), inset 0 1px 0 var(--glossy-hi);
transition: transform 0.07s var(--snap-fast), box-shadow 0.07s var(--snap-fast);
}
Active (Pressed) State
.gel-btn:active {
transform: translateY(2px);
box-shadow: 0 1px 0 var(--border-deep), inset 0 1px 0 transparent;
}
Selected State
.gel-btn.active { color: var(--blue-info); }
.gel-btn.active::after {
content: ''; position: absolute; bottom: 5px;
left: 50%; transform: translateX(-50%);
width: 18px; height: 2px; background: var(--blue-info); border-radius: 1px;
}
Color Variants
.gel-btn.blue { color: var(--blue-info); }
.gel-btn.green { color: var(--green-on); }
.gel-btn.pink { color: var(--pink-action); }
Disabled State
.gel-btn[disabled] { opacity: 0.4; pointer-events: none; }
HTML Structure
<!-- Basic -->
<button class="gel-btn">GEL</button>
<!-- Color variants -->
<button class="gel-btn blue">BLUE</button>
<button class="gel-btn green">GREEN</button>
<button class="gel-btn pink">PINK</button>
<!-- Selected -->
<button class="gel-btn active">SELECTED</button>
<!-- Sizes -->
<button class="gel-btn xs">XS</button>
<button class="gel-btn lg">LARGE</button>
Size Variants
| Size | Height | Min-Width | Font-Size | Padding |
|---|---|---|---|---|
.xs |
28px | 44px | 9px | 0 10px |
.sm |
32px | 52px | 10px | 0 12px |
| default | 38px | 64px | 11px | 0 16px |
.lg |
44px | 80px | 13px | 0 20px |
Material Variants
Single material: glossy polycarbonate. The two-layer background is the defining characteristic.
Theme Behavior
--clear-glassswaps: dark =rgba(255,255,255,0.08), light =rgba(255,255,255,0.5)--glossy-hiremains constant atrgba(255,255,255,0.14)- Surface colors swap via tokens
Constraints
- The two-layer background is MANDATORY. A single gradient cannot produce the gel look.
- The catch-light MUST fade to
transparent(not to a color) so the underlying body gradient shows through. glossy-hiMUST be0.14opacity, not0.06. This is the primary differentiator from rubber buttons.- Bottom shadow MUST be 3px (not 2px) because polycarbonate buttons sit taller than panel buttons.
- Press travel is 2px (not 1px like push-btn) due to the thicker keycap.
Accessibility
- Uses native
<button>element - Keyboard: Enter/Space to activate
- Focus: Browser default focus ring preserved
- Color variants are decorative; do not rely on color alone for state
♿
Accessibility
- Element
- Use native <button> element
- Keyboard
- Enter or Space to activate
- Focus
-
Visible focus indicator required. Use native browser focus ring or custom
:focus-visiblestyles.