Advertisements

User Controled Color Theme

Works on Desktop Safari, desktop Google Chrome, desktop Firefox 3.6-4, iPhone, iPod Touch, iPad.

So, in the last blog post I showed how to make RGB slides with HTML, CSS and some JavaScript. I thought about it and, while interesting, it doesn’t have a whole lot of practical application. Sure you could take that and hook up any other type of value to get whatever result you might need for your interface. Well that got me to thinking, so I threw together an implementation of the RGB sliders that allow a user to change the color scheme of a Web app. No need to spend time creating different color themes. Let the user do it.

OK, before you think I’m crazy, especially you folks from the design community, let me explain. I came up with a basic theme technique. I call it chromaeleon &mdash because the app’s chrome can change colors like a chamaeleon. The way this works is, instead of solid color gradients, you create gradients with transparent values of black and white. Behind this you have a background color which shows through the transparent gradients. This way, when the user drags the sliders, the background colors update and the look of the interface changes. Now in the real world you’d want to provide a way for the user to save their color choice. You could save the choice to localStorage. Then when the app loads, it checks to see it the user saved a color choice, if not, it goes to the default. Sorry, I didn’t do all of that. Just the part to update the background colors. Here’s what it will look like:

iPhone Chromaeleon Interface

The structure we’re going to use is pretty must standard as we’ve used elsewhere, a header, a section, some buttons.

<body>
	<header>
		<a href="http://css3wizardry.com" class="button back"><span class="pointer"></span><span>Back</span></a>
		<h1>Chromaeleon Theme</h1>
		<span class="button">Click Here</span>
	</header>
	<section>
		<h2>Use the sliders to adjust the colors of the theme.</h2>
		<div class="colorRow">
			<div id="redSlider" class="slider">
				<div class="thumb"></div>
			</div>
			<div id="redColor" class="colorOutput"></div>
			<span> Red</span>
		</div>
		<div class="colorRow">
			<div id="greenSlider" class="slider">
				<div class="thumb"></div>
			</div>
			<div id="greenColor" class="colorOutput"></div>
			<span> Green</span>
		</div>
		<div class="colorRow">
			<div id="blueSlider" class="slider">
				<div class="thumb"></div>
			</div>
			<div id="blueColor" class="colorOutput"></div>
			<span> Blue</span>
		</div>
		<div class="colorRow finalResult">
			<span>Final Color: </span>
			<div id="rgbColor" class="colorOutput"></div>
			<br />
			<span>RGB: </span><span id="rgbResult">0, 0, 0</span>
			<br />
			<span>HEX: </span><span id="hexResult">#000000</span>
		</div>
	</section>
</body>

So, for the header and the buttons, we need to change their default gradients, as I mentioned above, to have RGBA transparency values. This is my basic gradient:

background-image: 
	-moz-linear-gradient(top, 
		rgba(255,255,255,.5), 
		rgba(30,30,30,.65) 50%, 
		rgba(0,0,0,.7) 50%, 
		rgba(0,0,0,.8)); 
background-image: 
	-webkit-gradient(linear, left top, left bottom, 
		from(rgba(255,255,255,.5)), 
		color-stop(0.5,rgba(30,30,30,.65)), 
		color-stop(0.5, rgba(0,0,0,.7)), 
		to(rgba(0,0,0,.8)));

And for the hover state of the button, we use this gradient:

background-image: 
	-webkit-gradient(linear, left top, left bottom, 
		from(rgba(0,0,0,.1)), 
		color-stop(0.5,rgba(0,0,0,.5)), 
		color-stop(0.5, rgba(0,0,0,.6)), 
		to(rgba(255,255,255,.2)));
background-image: 
	-moz-linear-gradient(top,
		rgba(0,0,0,.1), 
		rgba(0,0,0,.5) 50%, 
		rgba(0,0,0,.6) 50%, 
		rgba(255,255,255,.2));

Now to change the color, all we need to do is introduce a new method to our existing code:

/**
*
* Method to update chrome colors according to the RGB value of the sliders.
*
*/
$.updateInterfaceColors = function() {
	$("header").css("background-color: rgb(" + $.rgbColor[0] + "," + $.rgbColor[1] + "," + $.rgbColor[2] + ")");
	$$(".button").forEach(function(button) {
		button.css("background-color: rgb(" + $.rgbColor[0] + "," + $.rgbColor[1] + "," + $.rgbColor[2] + ")");
	});
	$("section").css("background-color: rgb(" + $.rgbColor[0] + "," + $.rgbColor[1] + "," + $.rgbColor[2] + ")");
	$(".pointer").css("background-color: rgb(" + $.rgbColor[0] + "," + $.rgbColor[1] + "," + $.rgbColor[2] + ")");
};

To execute this method, we invoke it in the slide mouse event handlers for each slider, and for touch-based mobile devices we invoke it in the updateSliderTouch method:

/**
*
* This is for the red slider's mouse interaction, you'd do the same for the green and blue sliders' setup scripts as well.
*/
// Set up three sliders for Red, Green and Blue:
$.slider("#redSlider", { 
	onDrag : function() {
		$("#redSlider").setColorFromSlider("red");
		$.updateInterfaceColors();
	},
	// onDragEnd function necessary to remove hover state off of slider thumb when drag ends.
	onDragEnd : function() {},
	top : -6
});
/**
*
* This is for touch-enabled devices. You invoke the $.updateInterfaceColors() method just once inside the updateSliderTouch method's definition, at the very end.
*/
Element.prototype.updateSliderTouch = function( color ) {
	this.style.left =  curX + 'px'; 
	if (color === "red") {
		$("#" + color + "Color").css("background-color: rgb(" + curX +",0,0)");
		$.rgbColor[0] = curX;
	}
	if (color === "green") {
		$("#" + color + "Color").css("background-color: rgb(0," + curX +",0)");
		$.rgbColor[1] = curX;
	}
	if (color === "blue") {
		$("#" + color + "Color").css("background-color: rgb(0,0," + curX +")");
		$.rgbColor[2] = curX;
	}
	$("#" + color + "Slider").css("-webkit-background-size:" + (curX + 1) + "px 9px, 100% 9px");
	$("#" + color + "Slider").css("background-size:" + (curX + 1) + "px 9px, 100% 9px");
	$("#rgbColor").css("background-color: rgb(" + $.rgbColor[0] + "," + $.rgbColor[1] + "," + $.rgbColor[2] + ")");
	$("#rgbResult").fill($.rgbColor[0] + ", " + $.rgbColor[1] + ", " + $.rgbColor[2]);
	$("#hexResult").fill("#" + $.rgb2hex($.rgbColor[0]) + $.rgb2hex($.rgbColor[1]) + $.rgb2hex($.rgbColor[2]));
	$.updateInterfaceColors();
};

This works great on desktop Safari, Chrome and even Firefox (Yay!), and fine on the iPad. For iPod Touch or iPhone you need to load it in portrait mode. it’s a bit cramped due to the size of the sliders. I needed them to be at least 255px long for the RGB values, and then borders, box shadows and the extra space for the thumbs made them barely fit in the iPhone’s and iPod Touch’s default width. Try hitting the plus icon at the bottom of the browser and save it to you device’s desktop. Then run it from there, you’ll have more vertical space. You can try this out online or download the source code. Enjoy!

Advertisements

Range Slider with CSS and JavaScript

Works on desktop Safari, desktop Chrome, desktop Firefox 3.5 – 4, iPhone, iPod Touch, iPad.

In this post I’m going to show how to make a range slider using HTML, CSS and JavaScript that works with both a mouse and a finger. The mouse-enabled version required a small drag-and-drop JavaScript framework. Fortunately I had already put that together several years back. After spending some time playing around with touch events on mobile Webkit, I was able to come up with a way to implement horizontal dragging for the range slider.
range slider

I’m not going to go into details about my mouse-enable drag-and-drop framework. You can popup it open and read the copious comments in the example. However, I will explain how I implemented the touch-enabled drag for the slider.

The structure for a slider is fairly straightforward. You need a track and a thumb:

<div id="redSlider" class="slider">
	<div class="thumb"></div>
</div>

Since the structure is so simple, you might be wondering how we give it the look. The thumb gets border radius to make it round, along with a box shadow and a background-gradient, including a blue background gradient for hover. The slider track is styled with two background gradients, the bottom-most gradient is the default grey which swans the width of the slider. Layered on top of the same track is a second, bluish gradient. By using CSS3’s background sizing property, we will dynamically resize it as the slider’s thumb is dragged.

Of course, just dragging a slider thumb back in forth is not suck a big deal. I therefore created three sliders implemented as RGB pickers. By dragging each thumb, you add or subtract from a red, green or blue value. Down below you’ll see the final RGB and Hex values.
RGB Slider

Here are the basic styles for the slider:

.slider {
	display: inline-block;
	-webkit-box-sizing: border-box;
	-moz-box-sizing: padding-box;
	box-sizing: padding-box;
	-webkit-box-shadow: 2px 2px 4px #666;
	-moz-box-shadow: 2px 2px 4px #666;
	box-shadow: 2px 2px 4px #666;
	height: 9px;
	width: 277px; 
	padding: 1px;
	-webkit-border-radius: 4px;
	-moz-border-radius: 4px;
	border-radius: 4px;
	background-image: 
		-webkit-gradient(linear, left top ,left bottom,
		   from(#0a3a86),
		   color-stop(.5, #4c8de7),
		   color-stop(.95, #6babf5),
		   to(#0a3a86)),
		-webkit-gradient(linear, left top ,left bottom,
			from(#919191),
			color-stop(.5, #f0f0f0),
			color-stop(.5, #fff),
			color-stop(.95, #fff),
			to(#919191));
	background-image: 
		-moz-linear-gradient(top,
		   #0a3a86,
		   #4c8de7 50%,
		   #6babf5 95%,
		   #0a3a86),
		-moz-linear-gradient(top,
			#919191,
			#f0f0f0 50%,
			#fff 50%,
			#fff 95%,
			#919191);
	background-repeat: no-repeat, repeat-x;
}
.thumb {
	position:relative;
	-webkit-box-shadow: 2px 2px 3px #666;
	-moz-box-shadow: 2px 2px 3px #666;
	box-shadow: 2px 2px 3px #666;
	height:20px;
	width:20px;
	left: 0px; 
	top: -6px;
	-webkit-border-radius: 10px;
	-moz-border-radius: 10px;
	border-radius: 10px;
	background-image: 
	   -webkit-gradient(linear, left top, left bottom,
		   from(#aaa),
		   color-stop(.5, #ddd),
		   to(#ccc));
	background-image: 
	   -moz-linear-gradient(top,
		   #aaa,
		   #ddd 50%,
		   #ccc);
	cursor: move;
	-webkit-tap-highlight-color: transparent;
}
.thumb:hover, .thumb.hover {
	background-image: 
	   -webkit-gradient(linear, left top, left bottom,
		   from(#6297f2),
		   color-stop(.5, #0251ae),
		   to(#6297f2));
	background-image: 
	   -moz-linear-gradient(top,
		   #6297f2,
		   #0251ae 50%,
		   #6297f2);
}

Notice the slider’s background gradient style. The first background gradient will be the top-most. The last will be the bottom-most. But the top-most is going to be the blue part of the track that appears to the left of the thumb as it is dragged away from the left start of the range slider.

We also need some styles to set the initial states of the three thumbs. Notice that I’ve used background sizing to control the two background gradients. The first is for the blue top-most gradient, the second is for the full width grey gradient.

#redSlider .thumb {
	left: 121px;
}
#redSlider {
	-webkit-background-size: 123px 9px, 100% 9px;
	-moz-background-size: 123px 9px, 100% 9px;
	background-size: 123px 9px, 100% 9px;
}
#greenSlider .thumb {
	left: 156px;
}
#greenSlider {
	-webkit-background-size: 158px 9px, 100% 9px;
	-moz-background-size: 158px 9px, 100% 9px;
	background-size: 158px 9px, 100% 9px;
}
#blueSlider .thumb {
	left: 230px;
}
#blueSlider {
	-webkit-background-size: 232px 9px, 100% 9px;
	-moz-background-size: 232px 9px, 100% 9px;
	background-size: 232px 9px, 100% 9px;
}

So, I’ve defined two gradients with different background repeats: background-repeat: no-repeat, repeat-x; and background sizing with values such as: 123px 9px, 100% 9px. 9px is the height of the slider track. The bottom-most gradient has a width of 100%, and the top-most bluish one gets a width of 123px. By using these values, with very little markup, we can create visually and functionally complex structures.

/**
* Touch enabled support:
*/
/**
*
* Method to set the colors of color swatches and width of the slider progress track when the slider thumb is dragged.
*/
Element.prototype.setupSliderTouch = function( event ) {
	event.preventDefault();
	var el = event.target;
	var touch = event.touches[0];
	curX = touch.pageX - this.parentNode.offsetLeft;
	if (curX <= 0) { 
		curX = 0;
	}
	if (curX > 255) {
		curX = 255;
	}
};
Element.prototype.updateSliderTouch = function( color ) {
	this.style.left =  curX + 'px'; 
	if (color === "red") {
		$("#" + color + "Color").css("background-color: rgb(" + curX +",0,0)");
		$.rgbColor[0] = curX;
	}
	if (color === "green") {
		$("#" + color + "Color").css("background-color: rgb(0," + curX +",0)");
		$.rgbColor[1] = curX;
	}
	if (color === "blue") {
		$("#" + color + "Color").css("background-color: rgb(0,0," + curX +")");
		$.rgbColor[2] = curX;
	}
	
	$("#" + color + "Slider").css("-webkit-background-size:" + (curX + 1) + "px 9px, 100% 9px");
	$("#" + color + "Slider").css("background-size:" + (curX + 1) + "px 9px, 100% 9px");
	
	$("#rgbColor").css("background-color: rgb(" + $.rgbColor[0] + "," + $.rgbColor[1] + "," + $.rgbColor[2] + ")");
	$("#rgbResult").fill($.rgbColor[0] + ", " + $.rgbColor[1] + ", " + $.rgbColor[2]);
	$("#hexResult").fill("#" + $.rgb2hex($.rgbColor[0]) + $.rgb2hex($.rgbColor[1]) + $.rgb2hex($.rgbColor[2]));
};

$("#redSlider > .thumb").bind('touchmove', function(event) {
	this.setupSliderTouch(event);
	this.updateSliderTouch("red");
});
$("#greenSlider > .thumb").bind('touchmove', function(event) {
	this.setupSliderTouch(event);
	this.updateSliderTouch("green");
});
$("#blueSlider > .thumb").bind('touchmove', function(event) {
	this.setupSliderTouch(event);
	this.updateSliderTouch("blue");
});

Basically, I attach a touchmove event to the slider thumbs. The event listener passes the event to the setupSliderTouch method. The first thing the setupSliderTouch method does is to prevent the default interaction from taking place, such as page scrolling. We want the user to be able to move the thumb without scrolling the page. From the event passed in to setupSliderTouch we get the touch event and calculate its x coordinate on the screen. To calculate the touch’s position in relation to the slider, we subtract the left offset of the slider from the pageX of the touch. This gives us the left-most edge of the slider’s thumb. We store this as curX. We check the value of curX. If it is less than zero, we set it back to zero. We do this because this value will be used to set the position of the thumb and one of the RGB values. We don’t want either the thumb being dragged off of the left edge of the slider, nor a value less than zero, since RGB values start at zero. We do the same thing when the curX value is greater than 255 for the same reasons.

The updateSliderTouch method uses the value of the slider’s thumb to calculate and update RGB and Hex values, giving the user visual feedback as the thumb is dragged. Then we use the value of curX to update the background size of the blues background gradient on the slider track:

$("#" + color + "Slider").css("-webkit-background-size:" + (curX + 1) + "px 9px, 100% 9px");
$("#" + color + "Slider").css("background-size:" + (curX + 1) + "px 9px, 100% 9px");

That’s all there is to it. You can try this out online using desktop Safari, Chrome, or Firefox for the mouse version, or on an iPhone, iPod Touch or iPad for the touch version. Or you can download the source code, which I recommend, so you can dig into the CSS and JavaScirpt.

Update: September 15, 2010
If you’re trying this out on an iPhone, I noticed that there seems to be a very slight delay before an initial touch is registered on the screen. This means that in order to slide the thumb, you need to press and hold for a very brief moment before sliding, otherwise no touch gets registered and nothing happens. This doesn’t seem to happen when performing the same action on the iPad. Touches seem more responsive.