What I am looking for:
A way to style one HALF of a character. (In this case, half the letter being transparent)
What I have currently searched for and tried (With no luck):
Below is an example of what I am trying to obtain.
Does a CSS or JavaScript solution exist for this, or am I going to have to resort to images? I would prefer not to go the image route as this text will end up being generated dynamically.
UPDATE:
Since many have asked why I would ever want to style half of a character, this is why. My city had recently spent $250,000 to define a new "brand" for itself. This logo is what they came up with. Many people have complained about the simplicity and lack of creativity and continue to do so. My goal was to come up with this website as a joke. Type in 'Halifax' and you will see what I mean.
This question is related to
javascript
html
css
A nice WebKit-only solution that takes advantage of the background-clip: text
support: http://jsfiddle.net/sandro_paganotti/wLkVt/
span{
font-size: 100px;
background: linear-gradient(to right, black, black 50%, grey 50%, grey);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
How about something like this for shorter text?
It could even work for longer text if you did something with a loop, repeating the characters with JavaScript. Anyway, the result is something like this:
p.char {_x000D_
position: relative;_x000D_
display: inline-block;_x000D_
font-size: 60px;_x000D_
color: red;_x000D_
}_x000D_
_x000D_
p.char:before {_x000D_
position: absolute;_x000D_
content: attr(char);_x000D_
width: 50%;_x000D_
overflow: hidden;_x000D_
color: black;_x000D_
}
_x000D_
<p class="char" char="S">S</p>_x000D_
<p class="char" char="t">t</p>_x000D_
<p class="char" char="a">a</p>_x000D_
<p class="char" char="c">c</p>_x000D_
<p class="char" char="k">k</p>_x000D_
<p class="char" char="o">o</p>_x000D_
<p class="char" char="v">v</p>_x000D_
<p class="char" char="e">e</p>_x000D_
<p class="char" char="r">r</p>_x000D_
<p class="char" char="f">f</p>_x000D_
<p class="char" char="l">l</p>_x000D_
<p class="char" char="o">o</p>_x000D_
<p class="char" char="w">w</p>
_x000D_
If you are interested in this, then Lucas Bebber's Glitch is a very similar and super cool effect:
Created using a simple SASS Mixin such as
.example-one {
font-size: 100px;
@include textGlitch("example-one", 17, white, black, red, blue, 450, 115);
}
More details at Chris Coyer's CSS Tricks and Lucas Bebber's Codepen page
You can use below code. Here in this example I have used h1
tag and added an attribute data-title-text="Display Text"
which will appear with different color text on h1
tag text element, which gives effect halfcolored text as shown in below example
body {_x000D_
text-align: center;_x000D_
margin: 0;_x000D_
}_x000D_
_x000D_
h1 {_x000D_
color: #111;_x000D_
font-family: arial;_x000D_
position: relative;_x000D_
font-family: 'Oswald', sans-serif;_x000D_
display: inline-block;_x000D_
font-size: 2.5em;_x000D_
}_x000D_
_x000D_
h1::after {_x000D_
content: attr(data-title-text);_x000D_
color: #e5554e;_x000D_
position: absolute;_x000D_
left: 0;_x000D_
top: 0;_x000D_
clip: rect(0, 1000px, 30px, 0);_x000D_
}
_x000D_
<h1 data-title-text="Display Text">Display Text</h1>
_x000D_
This can be achieved with just CSS :before
selector and content property value
.
.halfed, .halfed1 {_x000D_
float: left;_x000D_
}_x000D_
_x000D_
.halfed, .halfed1 {_x000D_
font-family: arial;_x000D_
font-size: 300px;_x000D_
font-weight: bolder;_x000D_
width: 200px;_x000D_
height: 300px;_x000D_
position: relative; /* To help hold the content value within */_x000D_
overflow: hidden;_x000D_
color: #000;_x000D_
}_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
.halfed:before, .halfed1:before {_x000D_
width: 50%; /* How much we'd like to show */_x000D_
overflow: hidden; /* Hide what goes beyond our dimension */ _x000D_
content: 'X'; /* Halfed character */_x000D_
height: 100%;_x000D_
position: absolute;_x000D_
color: #28507D;_x000D_
_x000D_
}_x000D_
_x000D_
_x000D_
_x000D_
/* For Horizontal cut off */ _x000D_
_x000D_
.halfed1:before {_x000D_
width: 100%;_x000D_
height: 55%;_x000D_
_x000D_
}
_x000D_
<div class="halfed"> X </div>_x000D_
_x000D_
<div class="halfed1"> X </div>
_x000D_
FWIW, here's my take on this doing it only with CSS: http://codepen.io/ricardozea/pen/uFbts/
Several notes:
The main reason I did this was to test myself and see if I was able to accomplish styling half of a character while actually providing a meaningful answer to the OP.
I am aware that this is not an ideal or the most scalable solution and the solutions proposed by the people here are far better for "real world" scenarios.
The CSS code I created is based on the first thoughts that came to my mind and my own personal approach to the problem.
My solution only works on symmetrical characters, like X, A, O, M. **It does not work on asymmetric characters like B, C, F, K or lower case letters.
** HOWEVER, this approach creates very interesting 'shapes' with asymmetric characters. Try changing the X to a K or to a lower case letter like an h or a p in the CSS :)
HTML
<span class="half-letter"></span>
SCSS
.half-character {
display: inline-block;
font: bold 350px/.8 Arial;
position: relative;
&:before, &:after {
content: 'X'; //Change character here
display: inline-block;
width: 50%;
overflow: hidden;
color: #7db9e8;
}
&:after {
position: absolute;
top: 0;
left: 50%;
color: #1e5799;
transform: rotateY(-180deg);
}
}
I just played with @Arbel's solution:
var textToHalfStyle = $('.textToHalfStyle').text();_x000D_
var textToHalfStyleChars = textToHalfStyle.split('');_x000D_
$('.textToHalfStyle').html('');_x000D_
$.each(textToHalfStyleChars, function(i,v){_x000D_
$('.textToHalfStyle').append('<span class="halfStyle" data-content="' + v + '">' + v + '</span>');_x000D_
});
_x000D_
body{_x000D_
background-color: black;_x000D_
}_x000D_
.textToHalfStyle{_x000D_
display:block;_x000D_
margin: 200px 0 0 0;_x000D_
text-align:center;_x000D_
}_x000D_
.halfStyle {_x000D_
font-family: 'Libre Baskerville', serif;_x000D_
position:relative;_x000D_
display:inline-block;_x000D_
width:1;_x000D_
font-size:70px;_x000D_
color: black;_x000D_
overflow:hidden;_x000D_
white-space: pre;_x000D_
text-shadow: 1px 2px 0 white;_x000D_
}_x000D_
.halfStyle:before {_x000D_
display:block;_x000D_
z-index:1;_x000D_
position:absolute;_x000D_
top:0;_x000D_
width: 50%;_x000D_
content: attr(data-content); /* dynamic content for the pseudo element */_x000D_
overflow:hidden;_x000D_
color: white;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>_x000D_
<span class="textToHalfStyle">Dr. Jekyll and M. Hide</span>
_x000D_
We'll do it using just CSS pseudo selectors!
This technique will work with dynamically generated content and different font sizes and widths.
HTML:
<div class='split-color'>Two is better than one.</div>
CSS:
.split-color > span {
white-space: pre-line;
position: relative;
color: #409FBF;
}
.split-color > span:before {
content: attr(data-content);
pointer-events: none; /* Prevents events from targeting pseudo-element */
position: absolute;
overflow: hidden;
color: #264A73;
width: 50%;
z-index: 1;
}
To wrap the dynamically generated string, you could use a function like this:
// Wrap each letter in a span tag and return an HTML string
// that can be used to replace the original text
function wrapString(str) {
var output = [];
str.split('').forEach(function(letter) {
var wrapper = document.createElement('span');
wrapper.dataset.content = wrapper.innerHTML = letter;
output.push(wrapper.outerHTML);
});
return output.join('');
}
// Replace the original text with the split-color text
window.onload = function() {
var el = document.querySelector('.split-color'),
txt = el.innerHTML;
el.innerHTML = wrapString(txt);
}
Closest I can get:
$(function(){_x000D_
$('span').width($('span').width()/2);_x000D_
$('span:nth-child(2)').css('text-indent', -$('span').width());_x000D_
});
_x000D_
body{_x000D_
font-family: arial;_x000D_
}_x000D_
span{_x000D_
display: inline-block;_x000D_
overflow: hidden;_x000D_
}_x000D_
span:nth-child(2){_x000D_
color: red;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<span>X</span><span>X</span>
_x000D_
Demo: http://jsfiddle.net/9wxfY/2/
Heres a version that just uses one span: http://jsfiddle.net/9wxfY/4/
Limited CSS and jQuery Solution
I am not sure how elegant this solution is, but it cuts everything exactly in half: http://jsfiddle.net/9wxfY/11/
Otherwise, I have created a nice solution for you... All you need to do is have this for your HTML:
Take a look at this most recent, and accurate, edit as of 6/13/2016 : http://jsfiddle.net/9wxfY/43/
As for the CSS, it is very limited... You only need to apply it to :nth-child(even)
$(function(){_x000D_
var $hc = $('.half-color');_x000D_
var str = $hc.text();_x000D_
$hc.html("");_x000D_
_x000D_
var i = 0;_x000D_
var chars;_x000D_
var dupText;_x000D_
_x000D_
while(i < str.length){_x000D_
chars = str[i];_x000D_
if(chars == " ") chars = " ";_x000D_
dupText = "<span>" + chars + "</span>";_x000D_
_x000D_
var firstHalf = $(dupText);_x000D_
var secondHalf = $(dupText);_x000D_
_x000D_
$hc.append(firstHalf)_x000D_
$hc.append(secondHalf)_x000D_
_x000D_
var width = firstHalf.width()/2;_x000D_
_x000D_
firstHalf.width(width);_x000D_
secondHalf.css('text-indent', -width);_x000D_
_x000D_
i++;_x000D_
}_x000D_
});
_x000D_
.half-color span{_x000D_
font-size: 2em;_x000D_
display: inline-block;_x000D_
overflow: hidden;_x000D_
}_x000D_
.half-color span:nth-child(even){_x000D_
color: red;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<div class="half-color">This is a sentence</div>
_x000D_
Here is a CSS only solution for a full line of text, not just a character element.
div {
position: relative;
top: 2em;
height: 2em;
text-transform: full-width;
}
div:before,
div:after {
content: attr(data-content);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
div:after {
color: red;
/* mask for a single character. By repeating this mask, all the string becomes masked */
-webkit-mask-image: linear-gradient(to right, transparent 0, transparent .5em, white .5em, white 1em);
-webkit-mask-repeat: repeat-x; /* repeat the mask towards the right */
-webkit-mask-size: 1em; /* relative width of a single character */
/* non-vendor mask settings */
mask-image: linear-gradient(to right, transparent 0, transparent .5em, white .5em, white 1em);
mask-repeat: repeat-x;
mask-size: 1em;
}
/* demo purposes */
input[name="fontSize"]:first-of-type:checked ~ div {
font-size: 1em;
}
input[name="fontSize"]:first-of-type + input:checked ~ div {
font-size: 2em;
}
input[name="fontSize"]:first-of-type + input + input:checked ~ div {
font-size: 3em;
}
_x000D_
Font-size:
<input type="radio" name="fontSize" value="1em">
<input type="radio" name="fontSize" value="2em" checked>
<input type="radio" name="fontSize" value="3em">
<div data-content="A CSS only solution..."></div>
<div data-content="Try it on Firefox!"></div>
_x000D_
The idea is to apply an horizontal CSS mask for each character, that hides the first half of it [0 - 0.5em] and shows the second half [0.5em - 1em].
The width of the mask is mask-size: 1em
to match the width of the very first character in the string.
By using the mask-repeat: repeat-x
, the same mask is applied to the second, third character and so on.
I thought that using the font monospace
would solve the problem of using same-width letters, but I was wrong.
Instead, I solved it by using the text-transform: full-width
, that unfortunatelly is only supported by Firefox, I believe.
The use of relative unit em
allows the design to scale up/down depending on the font-size
.
If Firefox is not an option, then use this script for the rescue.
It works by inserting a child span
for each character. Inside each span, a non-repeated CSS mask is placed from [0% - 50%] and [50% - 100%] the width of the letter (which is the width of the span element).
This way we don't have anymore the restriction of using same-width characters.
const
dataElement = document.getElementById("data"),
content = dataElement.textContent,
zoom = function (fontSize) {
dataElement.style['font-size'] = fontSize + 'em';
};
while (dataElement.firstChild) {
dataElement.firstChild.remove()
}
for(var i = 0; i < content.length; ++i) {
const
spanElem = document.createElement('span'),
ch = content[i];
spanElem.setAttribute('data-ch', ch);
spanElem.appendChild(document.createTextNode(ch === ' ' ? '\u00A0' : ch));
data.appendChild(spanElem);
}
_x000D_
#data {
position: relative;
top: 2em;
height: 2em;
font-size: 2em;
}
#data span {
display: inline-block;
position: relative;
color: transparent;
}
#data span:before,
#data span:after {
content: attr(data-ch);
display: inline-block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
text-align: center;
color: initial;
}
#data span:after {
color: red;
-webkit-mask-image: linear-gradient(to right, transparent 0, transparent 50%, white 50%, white 100%);
mask-image: linear-gradient(to right, transparent 0, transparent 50%, white 50%, white 100%);
}
_x000D_
Font-size:
<input type="range" min=1 max=4 step=0.05 value=2 oninput="zoom(this.value)" onchange="zoom(this.value)">
<div id="data">A Fallback Solution...For all browsers</div>
_x000D_
Just for the record in history!
I've come up with a solution for my own work from 5-6 years ago, which is Gradext ( pure javascript and pure css, no dependency ) .
The technical explanation is you can create an element like this:
<span>A</span>
now if you want to make a gradient on text, you need to create some multiple layers, each individually specifically colored and the spectrum created will illustrate the gradient effect.
for example look at this is the word lorem inside of a <span>
and will cause a horizontal gradient effect ( check the examples ):
<span data-i="0" style="color: rgb(153, 51, 34);">L</span>
<span data-i="1" style="color: rgb(154, 52, 35);">o</span>
<span data-i="2" style="color: rgb(155, 53, 36);">r</span>
<span data-i="3" style="color: rgb(156, 55, 38);">e</span>
<span data-i="4" style="color: rgb(157, 56, 39);">m</span>
and you can continue doing this pattern for a long time and long paragraph as well.
What if you want to create a vertical gradient effect on texts?
Then there's another solution which could be helpful. I will describe in details.
Assuming our first <span>
again. but the content shouldn't be the letters individually; the content should be the whole text, and now we're going to copy the same ??<span>
again and again ( count of spans will define the quality of your gradient, more span, better result, but poor performance ). have a look at this:
<span data-i="6" style="color: rgb(81, 165, 39); overflow: hidden; height: 11.2px;">Lorem ipsum dolor sit amet, tincidunt ut laoreet dolore magna aliquam erat volutpat.</span>
<span data-i="7" style="color: rgb(89, 174, 48); overflow: hidden; height: 12.8px;">Lorem ipsum dolor sit amet, tincidunt ut laoreet dolore magna aliquam erat volutpat.</span>
<span data-i="8" style="color: rgb(97, 183, 58); overflow: hidden; height: 14.4px;">Lorem ipsum dolor sit amet, tincidunt ut laoreet dolore magna aliquam erat volutpat.</span>
<span data-i="9" style="color: rgb(105, 192, 68); overflow: hidden; height: 16px;">Lorem ipsum dolor sit amet, tincidunt ut laoreet dolore magna aliquam erat volutpat.</span>
<span data-i="10" style="color: rgb(113, 201, 78); overflow: hidden; height: 17.6px;">Lorem ipsum dolor sit amet, tincidunt ut laoreet dolore magna aliquam erat volutpat.</span>
<span data-i="11" style="color: rgb(121, 210, 88); overflow: hidden; height: 19.2px;">Lorem ipsum dolor sit amet, tincidunt ut laoreet dolore magna aliquam erat volutpat.</span>
what if you want to make these gradient effects to move and create an animation out of it?
well, there's another solution for it too. You should definitely check animation: true
or even .hoverable()
method which will lead to a gradient to start based on cursor position! ( sounds cool xD )
this is simply how we're creating gradients ( linear or radial ) on texts. If you liked the idea or want to know more about it, you should check the links provided.
Maybe this is not the best option, maybe not the best performant way to do this, but it will open up some space to create exciting and delightful animations to inspire some other people for a better solution.
It will allow you to use gradient style on texts, which is supported by even IE8!
Here you can find a working live demo and the original repository is here on GitHub as well, open source and ready to get some updates ( :D )
This is my first time ( yeah, after 5 years, you've heard it right ) to mention this repository anywhere on the Internet, and I'm excited about that!
[Update - 2019 August:] Github removed github-pages demo of that repository because I'm from Iran! Only the source code is available here tho...
I've just finished developing the plugin and it is available for everyone to use! Hope you will enjoy it.
First of all, make sure you have the jQuery
library is included. The best way to get the latest jQuery version is to update your head tag with:
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
After downloading the files, make sure you include them in your project:
<link rel="stylesheet" type="text/css" href="css/splitchar.css">
<script type="text/javascript" src="js/splitchar.js"></script>
All you have to do is to asign the class splitchar
, followed by the desired style to the element wrapping your text. e.g
<h1 class="splitchar horizontal">Splitchar</h1>
After all this is done, just make sure you call the jQuery function in your document ready file like this:
$(".splitchar").splitchar();
In order to make the text look exactly as you want it to, all you have to do is apply your design like this:
.horizontal { /* Base CSS - e.g font-size */ }
.horizontal:before { /* CSS for the left half */ }
.horizontal:after { /* CSS for the right half */ }
That's it! Now you have the Splitchar
plugin all set. More info about it at http://razvanbalosin.com/Splitchar.js/.
Here an ugly implementation in canvas. I tried this solution, but the results are worse than I expected, so here it is anyway.
$("div").each(function() {_x000D_
var CHARS = $(this).text().split('');_x000D_
$(this).html("");_x000D_
$.each(CHARS, function(index, char) {_x000D_
var canvas = $("<canvas />")_x000D_
.css("width", "40px")_x000D_
.css("height", "40px")_x000D_
.get(0);_x000D_
$("div").append(canvas);_x000D_
var ctx = canvas.getContext("2d");_x000D_
var gradient = ctx.createLinearGradient(0, 0, 130, 0);_x000D_
gradient.addColorStop("0", "blue");_x000D_
gradient.addColorStop("0.5", "blue");_x000D_
gradient.addColorStop("0.51", "red");_x000D_
gradient.addColorStop("1.0", "red");_x000D_
ctx.font = '130pt Calibri';_x000D_
ctx.fillStyle = gradient;_x000D_
ctx.fillText(char, 10, 130);_x000D_
});_x000D_
});
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<div>Example Text</div>
_x000D_
It may be irrelevant, maybe not, but sometime ago, I created a jQuery function that does the same thing, but horizontally.
I called it "Strippex" For 'stripe'+'text', demo : http://cdpn.io/FcIBg
I'm not saying this is the solution of any problems, but I already tried to apply css to half of a character, but horizontally, So the idea is the same, the realisation may be horrible, but it works.
Ah, and the most important, I had fun creating it !
.halfStyle {
position:relative;
display:inline-block;
font-size:68px; /* or any font size will work */
color: rgba(0,0,0,0.8); /* or transparent, any color */
overflow:hidden;
white-space: pre; /* to preserve the spaces from collapsing */
transform:rotate(4deg);
-webkit-transform:rotate(4deg);
text-shadow:2px 1px 3px rgba(0,0,0,0.3);
}
.halfStyle:before {
display:block;
z-index:1;
position:absolute;
top:-0.5px;
left:-3px;
width: 100%;
content: attr(data-content); /* dynamic content for the pseudo element */
overflow:hidden;
color: white;
transform:rotate(-4deg);
-webkit-transform:rotate(-4deg);
text-shadow:0 0 1px black;
}
http://experimental.samtremaine.co.uk/half-style/
You can crowbar this code into doing all sorts of interesting things - this is just one implementation my associate and I came up with last night.
Another CSS-only solution (though data-attribute is needed if you don't want to write letter-specific CSS). This one works more across the board (Tested IE 9/10, Chrome latest & FF latest)
span {_x000D_
position: relative;_x000D_
color: rgba(50,50,200,0.5);_x000D_
}_x000D_
_x000D_
span:before {_x000D_
content: attr(data-char);_x000D_
position: absolute;_x000D_
width: 50%;_x000D_
overflow: hidden;_x000D_
color: rgb(50,50,200);_x000D_
}
_x000D_
<span data-char="X">X</span>
_x000D_
You can also do it using SVG, if you wish:
var title = document.querySelector('h1'),_x000D_
text = title.innerHTML,_x000D_
svgTemplate = document.querySelector('svg'),_x000D_
charStyle = svgTemplate.querySelector('#text');_x000D_
_x000D_
svgTemplate.style.display = 'block';_x000D_
_x000D_
var space = 0;_x000D_
_x000D_
for (var i = 0; i < text.length; i++) {_x000D_
var x = charStyle.cloneNode();_x000D_
x.textContent = text[i];_x000D_
svgTemplate.appendChild(x);_x000D_
x.setAttribute('x', space);_x000D_
space += x.clientWidth || 15;_x000D_
}_x000D_
_x000D_
title.innerHTML = '';_x000D_
title.appendChild(svgTemplate);
_x000D_
<svg style="display: none; height: 100px; width: 100%" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">_x000D_
<defs id="FooDefs">_x000D_
<linearGradient id="MyGradient" x1="0%" y1="0%" x2="100%" y2="0%">_x000D_
<stop offset="50%" stop-color="blue" />_x000D_
<stop offset="50%" stop-color="red" />_x000D_
</linearGradient>_x000D_
</defs>_x000D_
<text y="50%" id="text" style="font-size: 72px; fill: url(#MyGradient)"></text>_x000D_
</svg>_x000D_
_x000D_
<h1>This is not a solution X</h1>
_x000D_
Edit (oct 2017):
background-clip
or ratherbackground-image options
are now supported by every major browser: CanIUse
Yes, you can do this with only one character and only CSS.
Webkit (and Chrome) only, though:
h1 {_x000D_
display: inline-block;_x000D_
margin: 0; /* for demo snippet */_x000D_
line-height: 1em; /* for demo snippet */_x000D_
font-family: helvetica, arial, sans-serif;_x000D_
font-weight: bold;_x000D_
font-size: 300px;_x000D_
background: linear-gradient(to right, #7db9e8 50%,#1e5799 50%);_x000D_
-webkit-background-clip: text;_x000D_
-webkit-text-fill-color: transparent;_x000D_
}
_x000D_
<h1>X</h1>
_x000D_
Visually, all the examples that use two characters (be it via JS, CSS pseudo elements, or just HTML) look fine, but note that that all adds content to the DOM which may cause accessibility--as well as text selection/cut/paste issues.
Source: Stackoverflow.com