// In progress.....
// TODO: Add additional lighting modes and more special key logic.
//
.container.keystate 
	span  
	
h1 #codevember keyboard inspired by my Ducky Channel Tuhaojin Shine 3 keyboard.

.keyboard.metal
	ul.top
		li.esc(data-key='esc') Esc
		li.margin-l F1
		li F2
		li F3
		li F4
		li.margin-l F5
		li F6
		li F7
		li F8
		li.margin-l F9
		li F10
		li F11
		li F12
		li.margin-l.pad-sm(data-key='print') PrtSc
		li.pad-sm(data-key='scrolllock') ScrLk
		li.pad-sm(data-key='pause') Pause
		li.margin-l.half(data-key='calculator') 
			i.fa.fa-calculator
		li(data-key='desktop')
			i.fa.fa-desktop
		li(data-key='mail')
			i.fa.fa-envelope-o
		li(data-key='home')
			i.fa.fa-home
			
	ul
		li.bl.backtick(data-alt='~') `
		li.bl.one(data-alt='!') 1
		li.bl.two(data-alt='@') 2
		li.bl.three(data-alt='#') 3
		li.bl.four(data-alt='$') 4
		li.bl.five(data-alt='%') 5
		li.bl.six(data-alt='^') 6
		li.bl.seven(data-alt='&') 7
		li.bl.eight(data-alt='*') 8
		li.bl.nine(data-alt='\u0028') 9
		li.bl.zero(data-alt='\u0029') 0
		li.bl.hyphen(data-alt='_') -
		li.bl.equals(data-alt='+') =
		li.backspace(data-key='backspace')
			i.fa.fa-long-arrow-left
		li.margin-l.pad-sm(data-key='insert') Ins
		li.pad-sm(data-key='home') Home
		li.pad-sm(data-key='pageup') PgUp
		li.margin-l.half.pad-sm(data-key='numlock') Num
		li /
		li *
		li(data-key='minus')
			i.fa.fa-minus
				
	ul
		li.tab(data-key='tab') Tab
		li Q
		li W
		li E
		li R
		li T
		li Y
		li U
		li I
		li O
		li P
		li.bl.squareleft(data-alt='\u007B') [
		li.bl.squareright(data-alt='\u007D') ]
		li.bl.backslash(data-alt='|') \
		li.margin-l.pad-sm.vol-off(data-key='backspace' data-function='mute') Del
			i.fa.fa-volume-off
		li.pad-sm.vol-down(data-key='end' data-function='vol-down') End
			i.fa.fa-volume-down
		li.pad-sm.vol-up(data-key='pagedown' data-function='vol-up') PgDn
			i.fa.fa-volume-up
		li.margin-l.half 7
		li 8
		li 9
		li.num-plus(data-key='plus')
			i.fa.fa-plus
		
	ul
		li.caps(data-key='caps') Caps
		li A
		li S
		li D
		li F
		li G
		li H
		li J
		li K
		li L
		li.bl.semicolon(data-alt=':') ;
		li.bl.apostrophe(data-alt='\u0022') '
		li.enter(data-key='enter') 
			i.fa.fa-sign-in
		li.wide 4
		li 5
		li 6
		
	ul
		li.shift(data-key='caps') 
			i.fa.fa-arrow-up
		li Z
		li X
		li C
		li V
		li B
		li N
		li M
		li.bl.comma(data-alt='<') ,
		li.bl.fullstop(data-alt='>') .
		li.bl.forwardslash(data-alt='?') /
		li.shift(data-key='caps')
			i.fa.fa-arrow-up
		li.pad-sm.arrow-up(data-key='up')
			i.fa.fa-caret-up
		li 1
		li 2
		li 3
		li.num-enter(data-key='enter')
			i.fa.fa-sign-in
			
	ul
		li.pad(data-key='ctrlleft') Ctrl
		li.pad(data-key='windows')
			i.fa.fa-windows
		li.pad(data-key='altleft') Alt
		li.spacebar &nbsp;
		li.pad(data-key='altright') Alt
		li.pad(data-key='windows')
			i.fa.fa-windows
		li.pad.fn(data-key='function') Fn
		li.pad(data-key='ctrlright') Ctrl
		li.margin-l.pad-sm(data-key='left')
			i.fa.fa-caret-left
		li.pad-sm(data-key='down')
			i.fa.fa-caret-down
		li.pad-sm(data-key='right')
			i.fa.fa-caret-right
		li.margin-l.half.zero-wide 0
		li(data-key='backspace') Del
		
.container.lighting-mode
	hr
	p Change keyboard lighting mode:
	form
		input#radio_pulse(type='radio' name='mode' value='pulse' checked='checked')
		label(for='radio_pulse') Pulse
		input#radio_lines(type='radio' name='mode' value='lines')
		label(for='radio_lines') Lines
		input#radio_wave(type='radio' name='mode' value='wave')
		label(for='radio_wave') Wave
		input#radio_none(type='radio' name='mode' value='none')
		label(for='radio_none') None
	hr
		
.container.text-area
	.msg Type something by clicking keys on the keyboard. The characters will appear here:
	.inner
	
		
.container
	p
		i Being made with 
			i.fa.fa-heart
			|  by Andrew Ashley 
			a(title='My Portfolio Website' target='_blank' href='https://www.andrewashley.co.nz')
				i.fa.fa-globe
			|  - aka 
			a(title='My CodePen' target='_blank' href='https://codepen.io/coinoperatedgoi/') @coinoperatedgoi 
				i.fa.fa-codepen
	
View Compiled
@m:20px;
@width:1032px;

body {
	font-family: 'Roboto', sans-serif;
	-moz-user-select:none;
	-webkit-user-select:none;
	user-select:none;	
}

.keyboard {
	letter-spacing:-1.5px;
	font-size:0.90em;
	width:@width;
	height:285px;
	border-radius:5px;
	margin:50px auto;
	
	&:after, &:before {
		content:"";
		clear:both;
		display:block;
	}	
}

.metal {	
  background-color: hsl(0,0%,90%);
  box-shadow:
    inset hsla(0,0%,100%,.7) 0  2px 1px 2px, /* top HL */    
    hsla(0,0%, 0%,.15) 0  2px 2px 2px, /* outer SD */
    hsla(0,0%,100%,.5) 0  2px 2px 2px; /* outer HL */ 	
	
  background-image: -webkit-repeating-linear-gradient(top, hsla(0,0%,100%,0) 0%, hsla(0,0%,100%,0)   6%, hsla(0,0%,100%, .1) 7.5%),
    -webkit-repeating-linear-gradient(top, hsla(0,0%,  0%,0) 0%, hsla(0,0%,  0%,0)   4%, hsla(0,0%,  0%,.03) 4.5%),
    -webkit-repeating-linear-gradient(top, hsla(0,0%,100%,0) 0%, hsla(0,0%,100%,0) 1.2%, hsla(0,0%,100%,.15) 2.2%),
    
    linear-gradient(180deg, hsl(0,0%,78%)  0%, 
    hsl(0,0%,90%) 47%, 
    hsl(0,0%,78%) 53%,
    hsl(0,0%,70%)100%);
}

ul { // Row
	margin:0 10px;
	padding:0;
	list-style:none;
	position:relative;
	color: rgba(255,255,255,1);
	
	&.top {
		padding-top:15px;
	}
	
	&.bottom {
		padding-bottom:15px;
	}
	
	&:after, &:before {
		content:"";
		clear:both;
		display:block;
	}
	
	&:nth-child(1) {
		margin-bottom:10px;
	}
}

li { // Key
	cursor:pointer;
	position:relative;
	z-index:1000;
	text-align:center;
	float: left;
	background:rgba(0,0,0,1);
	padding:5px 10px 14px 10px;
	margin:0 2px 4px 2px;
	min-width:22px;
	border-radius:5px;
	box-shadow: 0 4px 4px rgba(0,0,0,1),
							inset 0 0 4px #808080,
							inset 0 1px 0 #d1d1d1;
		
	&.active {
		cursor:pointer;
		top:3px;
		box-shadow: 0 1px 1px rgba(0,0,0,1),
								inset 0 0 4px #808080,
								inset 0 1px 0 #d1d1d1;
	}
}

.margin-l {
	margin-left:@m;
	&.half {
		margin-left:@m / 2;
	}
}

.margin-r {
	margin-right:@m;
	&.half {
		margin-right:@m / 2;
	}
}

.pad {
	padding-left:16px;
	padding-right:16px;
}

.pad-sm {
	padding-left:1px !important;
	padding-right:1px !important;
	min-width:39px;
}

.wide {
	margin-left:163px;
}

.esc {
	margin-right:@m - 5;
}

.backspace {
	min-width:44px;
}

.tab {
	min-width:44px;
}

.caps {
	min-width:56px;
}

.enter {
	min-width:56px;
}

.shift {
	min-width:79px;
}

.spacebar {
	min-width:236px;
}

.arrow-up {
	margin-left:66px;
	margin-right:54px;
}

.num-plus,
.num-enter {
	position:absolute;
	right:1px;
	min-height:60px;
	line-height:60px;
}

.zero-wide {
	min-width:68px;
}

.vol-up {
	> i {
		position:absolute;
		bottom:3px; left:17px;
		font-size:11px;
	}
}

.vol-down {
	> i {
		position:absolute;
		bottom:3px; left:18px;
		font-size:11px;
	}
}

.vol-off {
	> i {
		position:absolute;
		bottom:3px; left:19px;
		font-size:11px;
	}
}

.bl {
	padding:12px 10px 7px 10px;
	text-align:left;
}

.bl {
	&:after {
		font-size:0.85em;
		display:block;
		position:absolute;
		top:2px; right:8px;
		content:attr(data-alt);
	}
	
	&.hyphen:after {
		top:-1px;
	}
	
	&.backslash:after {
		top:3px;
		right:10px;
	}
	
	// Third row
	&.semicolon:after {
		right:10px;
	}
	
	&.apostrophe:after {
		right:10px;
	}
	
	// Forth row
	&.comma:after {
		right:10px;
	}
	
	&.fullstop:after {
		right:10px;
	}
}

.container {
	color:lighten(#000,15%);
	max-width:@width;
	width:100%;
	margin:20px auto 30px auto;
	
	&.lighting-mode {
		padding:10px;
		
		p {
			font-weight:700;
		}
	}
	
	&.text-area {
		background:#dedede;		
		border-radius:10px;
		
		.msg {
			padding:10px;
			font-weight:700;
		}
		
		.inner {
			padding:10px;
		}
	}
}

.text-center {
	text-align:center;
}

.fa-heart {
	color:lighten(#ff0000,15%);
}

h1 {
	font-family: 'Pacifico', cursive;
	text-align:center;
	font-weight:400;
	color:#38393a;
	text-shadow:2px 2px 6px rgba(0,0,0,0.3);
}

p {
	margin:5px 0;
	color:#38393a;
	
	a {
		color:#2d74bc;
		
		&:hover {
			color:#38393a;
		}
	}
}

input {
	margin-left:0;
	display:none;
	
	&:checked + label {
		font-weight:700;
		text-decoration:underline;
		color:#ff0000;
	}
}

label {
	margin-right:15px;
	
	&:hover {
		cursor:pointer;
		text-decoration:underline;
	}
}

.on {
	color:rgba(255,255,255,1) !important;
}

.lowercase {
	text-transform:lowercase;
}

.keystate {
	visibility:hidden;
	margin-top:0;
	margin-bottom:0;
	
	span {
		background: lighten(#ff0000, 20%);
		color:#fff;
		border-radius:5px;
		padding:3px 10px;
		font-size:13px;
	}
}
View Compiled
// TODO: Add more lighting functions
var $keystate = $('.keystate span');

function removeStyles() {
	$('ul, li').removeAttr('style');
}

function resetTimelines() {
	pulseTl.pause(); pulseTl.time(0); 
	linesTl.pause(); linesTl.time(0);
	waveTl.pause(); waveTl.time(0);
}

// Setup Timelines
var pulseTl = new TimelineMax({ repeat:-1, yoyo:true, paused:true });
pulseTl.to('ul', 1.5, { color:'rgba(255,255,255,0.3)', delay:0.5 });

var linesTl = new TimelineMax({ repeat:-1, paused:true });
linesTl.staggerFrom('li', 1, { color:'rgba(255,255,255,0.3)' }, 0.1);

var waveTl = new TimelineMax({ repeat:-1, yoyo:true, paused:true });
waveTl.staggerTo('ul', 1, { color:'rgba(255,255,255,0.3)' }, 0.3);

// Kick off
removeStyles();
pulseTl.play();


// Select Mode
$('input:radio[name=mode]').change(function(e) {	
	if($(this).val() == 'pulse') {		
		resetTimelines();
		removeStyles();
		pulseTl.play();
	}	
	
	if($(this).val() == 'lines') {
		resetTimelines();
		removeStyles();
		TweenMax.set('li', { color:'rgba(255,255,255,0.3)' })
		linesTl.play();
	}		

	if($(this).val() == 'wave') {		
		resetTimelines();
		TweenMax.set('li', { color:'rgba(255,255,255,0)' });
		removeStyles();		
		waveTl.play();
	}		
	
	if($(this).val() == 'none') {
		resetTimelines();
		removeStyles();
	}			
	
});

$('li').on('click', function() {
	// TODO: Add even more logic for special keys...
	var dataKey = $(this).attr('data-key');
	var $textArea = $('.text-area .inner');
	
	if ($(this).attr('data-key') != undefined) {
		if(dataKey == 'backspace') {
			var text = $('.text-area .inner').text();
			var trimmed = text.substring(0,text.length-1);
			$textArea.text(trimmed);	
		}
		
		if(dataKey == 'caps') {
			$('.caps').toggleClass('on');
			if($('.caps').hasClass('on')) { 
				$keystate.text('Caps on');
				$keystate.css('visibility','visible');
			} else {
				$keystate.css('visibility','hidden');
			}
		}
		
		if(dataKey == 'function') {
			$('.fn').toggleClass('on');
			if($('.fn').hasClass('on')) { 
				$keystate.text('Function on');
				$keystate.css('visibility','visible');
			} else {
				$keystate.css('visibility','hidden');
			}			
		}
		
		if(dataKey == 'enter') {
			$textArea.append('<br/>');	
		}	
		
		if(dataKey == 'tab') {
			$textArea.append('&nbsp;&nbsp;&nbsp;&nbsp;');	
			// An actually tab '&#09;' isn't "tabby" enough for me.
		}			
		
		// Function Keys
		if($('.fn').hasClass('on') && $(this).attr('data-function') != undefined) {
			var keyFunction = $(this).attr('data-function');
			alert(keyFunction);
		}		
	} 
	else 
	// No data-key defined
	{
		var text = '';
		if($('.caps').hasClass('on')) {
			text = $(this).text();
			
			if($(this).hasClass('bl')) {
				// It's got additional character functions
				var altKey = $(this).attr('data-alt');
				text = altKey;
			}			
		}		
		else 
		{
			text = $(this).text().toLowerCase();
		}		
		$textArea.append(text);		
	}
	
});

$('li').on('mousedown', function(){
	$(this).addClass('active');
});

$('li').on('mouseup', function(){
	$(this).removeClass('active');
});

// Other Tweens
TweenMax.to('.fa-heart', 1.5, { color:'#b20e0e', repeat:-1, yoyo:true })



function randomNumber(min, max){
	return Math.floor(Math.random() * (1 + max - min) + min);
}
Run Pen

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css
  2. https://fonts.googleapis.com/css?family=Pacifico|Roboto:400,700

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js