ChatGPT解决这个技术问题 Extra ChatGPT

Is it possible to apply CSS to half of a character?

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):

Methods for styling half of a character/letter

Styling part of a character with CSS or JavaScript

Apply CSS to 50% of a character

Below is an example of what I am trying to obtain.

https://i.stack.imgur.com/SaH8v.png

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.

Comments are not for extended discussion; this conversation has been moved to chat.
They were asking "why" because they wanted to know why you would use CSS and not use SVG or an image editor. Nothing to do with the business decisions about their logo. As per your link to their logo, why didn't you just crop the X?
Please take accessibility into account before you apply some of these methods to your text. The methods mentioned below are highly discouraging because they don't enhance legibility and readability for people with poor vision, learning disabilities, aphasia, dyslexia or low adult literacy. There are guidelines which need to be followed and even google announced that will start ranking websites according to their compliance with the WCAG rules. It's easy to test against those with tools as axe. If an image/svg is used instead then both need a text alternative for the info being conveyed.

A
Arbel

Now on GitHub as a Plugin!

https://i.stack.imgur.com/Glo2Q.png

Demo | Download Zip | Half-Style.com (Redirects to GitHub)

Pure CSS for a Single Character

JavaScript used for automation across text or multiple characters

Preserves Text Accessibility for screen readers for the blind or visually impaired

Part 1: Basic Solution

https://i.stack.imgur.com/ZYic1.png

Demo: http://jsfiddle.net/arbel/pd9yB/1694/

This works on any dynamic text, or a single character, and is all automated. All you need to do is add a class on the target text and the rest is taken care of.

Also, the accessibility of the original text is preserved for screen readers for the blind or visually impaired.

Explanation for a single character:

Pure CSS. All you need to do is to apply .halfStyle class to each element that contains the character you want to be half-styled.

For each span element containing the character, you can create a data attribute, for example here data-content="X", and on the pseudo element use content: attr(data-content); so the .halfStyle:before class will be dynamic and you won't need to hard code it for every instance.

Explanation for any text:

Simply add textToHalfStyle class to the element containing the text.

// jQuery for automated mode jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); .halfStyle { position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ color: black; /* or transparent, any color */ overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ } .halfStyle:before { display: block; z-index: 1; position: absolute; top: 0; left: 0; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; color: #f00; }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo)

Part 2: Advanced solution - Independent left and right parts

https://i.stack.imgur.com/zYa0F.png

With this solution you can style left and right parts, individually and independently.

Everything is the same, only more advanced CSS does the magic.

jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); .halfStyle { position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ color: transparent; /* hide the base character */ overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ } .halfStyle:before { /* creates the left part */ display: block; z-index: 1; position: absolute; top: 0; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #f00; /* for demo purposes */ text-shadow: 2px -2px 0px #af0; /* for demo purposes */ } .halfStyle:after { /* creates the right part */ display: block; direction: rtl; /* very important, will make the width to start from right */ position: absolute; z-index: 2; top: 0; left: 50%; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #000; /* for demo purposes */ text-shadow: 2px 2px 0px #0af; /* for demo purposes */ }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo)

Part 3: Mix-Match and Improve

Now that we know what is possible, let's create some variations.

-Horizontal Half Parts

Without Text Shadow:

Possibility of Text Shadow for each half part independently:

// jQuery for automated mode jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); .halfStyle { position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ color: transparent; /* hide the base character */ overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ } .halfStyle:before { /* creates the top part */ display: block; z-index: 2; position: absolute; top: 0; height: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #f00; /* for demo purposes */ text-shadow: 2px -2px 0px #af0; /* for demo purposes */ } .halfStyle:after { /* creates the bottom part */ display: block; position: absolute; z-index: 1; top: 0; height: 100%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #000; /* for demo purposes */ text-shadow: 2px 2px 0px #0af; /* for demo purposes */ }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo)

-Vertical 1/3 Parts

Without Text Shadow:

Possibility of Text Shadow for each 1/3 part independently:

// jQuery for automated mode jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); .halfStyle { /* base char and also the right 1/3 */ position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ color: transparent; /* hide the base character */ overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ color: #f0f; /* for demo purposes */ text-shadow: 2px 2px 0px #0af; /* for demo purposes */ } .halfStyle:before { /* creates the left 1/3 */ display: block; z-index: 2; position: absolute; top: 0; width: 33.33%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #f00; /* for demo purposes */ text-shadow: 2px -2px 0px #af0; /* for demo purposes */ } .halfStyle:after { /* creates the middle 1/3 */ display: block; z-index: 1; position: absolute; top: 0; width: 66.66%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #000; /* for demo purposes */ text-shadow: 2px 2px 0px #af0; /* for demo purposes */ }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo)

-Horizontal 1/3 Parts

Without Text Shadow:

Possibility of Text Shadow for each 1/3 part independently:

// jQuery for automated mode jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); .halfStyle { /* base char and also the bottom 1/3 */ position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ color: transparent; overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ color: #f0f; text-shadow: 2px 2px 0px #0af; /* for demo purposes */ } .halfStyle:before { /* creates the top 1/3 */ display: block; z-index: 2; position: absolute; top: 0; height: 33.33%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #f00; /* for demo purposes */ text-shadow: 2px -2px 0px #fa0; /* for demo purposes */ } .halfStyle:after { /* creates the middle 1/3 */ display: block; position: absolute; z-index: 1; top: 0; height: 66.66%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #000; /* for demo purposes */ text-shadow: 2px 2px 0px #af0; /* for demo purposes */ }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo)

-HalfStyle Improvement By @KevinGranger

https://i.stack.imgur.com/PgdJ6.png

// jQuery for automated mode jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); body { background-color: black; } .textToHalfStyle { display: block; margin: 200px 0 0 0; text-align: center; } .halfStyle { font-family: 'Libre Baskerville', serif; position: relative; display: inline-block; width: 1; font-size: 70px; color: black; overflow: hidden; white-space: pre; text-shadow: 1px 2px 0 white; } .halfStyle:before { display: block; z-index: 1; position: absolute; top: 0; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; color: white; }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo)

-PeelingStyle improvement of HalfStyle by @SamTremaine

https://i.stack.imgur.com/r67KC.png

// jQuery for automated mode jQuery(function($) { var text, chars, $el, i, output; // Iterate over all class occurences $('.textToHalfStyle').each(function(idx, el) { $el = $(el); text = $el.text(); chars = text.split(''); // Set the screen-reader text $el.html('' + text + ''); // Reset output for appending output = ''; // Iterate over all chars in the text for (i = 0; i < chars.length; i++) { // Create a styled element for each character and append to container output += ''; } // Write to DOM only once $el.append(output); }); }); .halfStyle { position: relative; display: inline-block; font-size: 68px; color: rgba(0, 0, 0, 0.8); overflow: hidden; white-space: pre; transform: rotate(4deg); text-shadow: 2px 1px 3px rgba(0, 0, 0, 0.3); } .halfStyle:before { /* creates the left part */ display: block; z-index: 1; position: absolute; top: -0.5px; left: -3px; width: 100%; content: attr(data-content); overflow: hidden; pointer-events: none; color: #FFF; transform: rotate(-4deg); text-shadow: 0px 0px 1px #000; }

Single Characters:

X Y Z A

Automated:

Half-style, please.

(JSFiddle demo and on samtremaine.co.uk)

Part 4: Ready for Production

Customized different Half-Style style-sets can be used on desired elements on the same page. You can define multiple style-sets and tell the plugin which one to use.

The plugin uses data attribute data-halfstyle="[-CustomClassName-]" on the target .textToHalfStyle elements and makes all the necessary changes automatically.

So, simply on the element containing the text add textToHalfStyle class and data attribute data-halfstyle="[-CustomClassName-]". The plugin will do the rest of the job.

https://i.stack.imgur.com/RxjKF.png

Also the CSS style-sets' class definitions match the [-CustomClassName-] part mentioned above and is chained to .halfStyle, so we will have .halfStyle.[-CustomClassName-]

jQuery(function($) { var halfstyle_text, halfstyle_chars, $halfstyle_el, halfstyle_i, halfstyle_output, halfstyle_style; // Iterate over all class occurrences $('.textToHalfStyle').each(function(idx, halfstyle_el) { $halfstyle_el = $(halfstyle_el); halfstyle_style = $halfstyle_el.data('halfstyle') || 'hs-base'; halfstyle_text = $halfstyle_el.text(); halfstyle_chars = halfstyle_text.split(''); // Set the screen-reader text $halfstyle_el.html('' + halfstyle_text + ''); // Reset output for appending halfstyle_output = ''; // Iterate over all chars in the text for (halfstyle_i = 0; halfstyle_i < halfstyle_chars.length; halfstyle_i++) { // Create a styled element for each character and append to container halfstyle_output += ''; } // Write to DOM only once $halfstyle_el.append(halfstyle_output); }); }); /* start half-style hs-base */ .halfStyle.hs-base { position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ color: #000; /* for demo purposes */ } .halfStyle.hs-base:before { display: block; z-index: 1; position: absolute; top: 0; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ pointer-events: none; /* so the base char is selectable by mouse */ overflow: hidden; color: #f00; /* for demo purposes */ } /* end half-style hs-base */ /* start half-style hs-horizontal-third */ .halfStyle.hs-horizontal-third { /* base char and also the bottom 1/3 */ position: relative; display: inline-block; font-size: 80px; /* or any font size will work */ color: transparent; overflow: hidden; white-space: pre; /* to preserve the spaces from collapsing */ color: #f0f; text-shadow: 2px 2px 0px #0af; /* for demo purposes */ } .halfStyle.hs-horizontal-third:before { /* creates the top 1/3 */ display: block; z-index: 2; position: absolute; top: 0; height: 33.33%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #f00; /* for demo purposes */ text-shadow: 2px -2px 0px #fa0; /* for demo purposes */ } .halfStyle.hs-horizontal-third:after { /* creates the middle 1/3 */ display: block; position: absolute; z-index: 1; top: 0; height: 66.66%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; pointer-events: none; /* so the base char is selectable by mouse */ color: #000; /* for demo purposes */ text-shadow: 2px 2px 0px #af0; /* for demo purposes */ } /* end half-style hs-horizontal-third */ /* start half-style hs-PeelingStyle, by user SamTremaine on Stackoverflow.com */ .halfStyle.hs-PeelingStyle { position: relative; display: inline-block; font-size: 68px; color: rgba(0, 0, 0, 0.8); overflow: hidden; white-space: pre; transform: rotate(4deg); text-shadow: 2px 1px 3px rgba(0, 0, 0, 0.3); } .halfStyle.hs-PeelingStyle:before { /* creates the left part */ display: block; z-index: 1; position: absolute; top: -0.5px; left: -3px; width: 100%; content: attr(data-content); overflow: hidden; pointer-events: none; color: #FFF; transform: rotate(-4deg); text-shadow: 0px 0px 1px #000; } /* end half-style hs-PeelingStyle */ /* start half-style hs-KevinGranger, by user KevinGranger on StackOverflow.com*/ .textToHalfStyle.hs-KevinGranger { display: block; margin: 200px 0 0 0; text-align: center; } .halfStyle.hs-KevinGranger { font-family: 'Libre Baskerville', serif; position: relative; display: inline-block; width: 1; font-size: 70px; color: black; overflow: hidden; white-space: pre; text-shadow: 1px 2px 0 white; } .halfStyle.hs-KevinGranger:before { display: block; z-index: 1; position: absolute; top: 0; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow: hidden; color: white; } /* end half-style hs-KevinGranger

Half-style, please.

Half-style, please.

Half-style, please.

Half-style, please.

(JSFiddle demo)


Comments are not for extended discussion; this conversation has been moved to chat.
This is very cool. It's worth noting that this technique breaks word-wrapping, white-space, and character-spacing CSS.
T
TylerH

https://i.stack.imgur.com/TkwNq.png

I've just finished developing the plugin and it is available for everyone to use! Hope you will enjoy it.

View Project on GitHub - View Project Website. (so you can see all the split styles)

Usage

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>

Markup

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();

Customizing

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/.


IT would appear so! Although, one issue I can see. What is causing the gap between some letters? For example, the huge gap between the two D's.
Well, that is because of the spacings and margins. Anyway, I will look into this and improve it. (probably I'll make a 'plugin' ) Glad I could help :)
This has problems with text wrapping which is exhibited even in the latest fiddle. When one character wraps, it essentially splits into two. Should be a trivial fix, though.
Don't depend on jquery-latest.min.js, it can make your sites break without warning if jQuery is updated and the plugin does not work with the newer one. Instead: use a specific version and check compatibility when updating.
You might want to edit your answer to not suggest using jquery-latest.js. As @NielsBom mentioned, in the past it could break your site when it was updated. Since July of 2014, it won't do that but that is because it is locked at version 1.11.1 and will never be updated again.
T
TylerH

Yes, you can do this with only one character and only CSS:

http://jsbin.com/rexoyice/1/

h1 { display: inline-block; margin: 0; /* for demo snippet */ line-height: 1em; /* for demo snippet */ font-family: helvetica, arial, sans-serif; font-weight: bold; font-size: 300px; background: linear-gradient(to right, #7db9e8 50%,#1e5799 50%); background-clip: text; -webkit-text-fill-color: transparent; }

X

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.


WebKit has a history of rendering bugs that are almost IE6/IE7 levels of bizarre (you could even say that Safari and Chrome are the IE6 of the modern web), and behaving in ways that deviate from the standard for no particular reason. IE has been much better since version 9, so while the ancient versions ought to die already, I don't see any reason for the hate for its recent versions. And I certainly don't see why people support the idea of a WebKit/Blink monoculture (the comments here are probably in jest, but I've heard of people who seriously believe in it).
That being said, background-clip: text is super awesome and they should consider it (or something similar like text-decoration-background) for a level 4 module.
Yeah, I'd be happier if blink/webkit died and modern IE's+FF rendering engines survived than the other way around. Which isn't to say that the rest of chrome isn't nice, just that its rendering is pretty much the worst of the bunch nowadays.
@DA: Sure. Blink is still very similar to webkit, however, and the dubious behavior isn't all post-fork, so I'm no so sure it's worth making the distinction very strongly. Nevertheless, safari does appear slightly less buggy.
If you remove display: inline-block; from the css, then selection works nicely with multiple characters, e.g. <h1>X</h1><h1>Y</h1><h1>Z</h1>.
C
Community

https://i.stack.imgur.com/oBWFs.png

JSFiddle DEMO

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);
}

m
m4n0

Here an ugly implementation in canvas. I tried this solution, but the results are worse than I expected, so here it is anyway.

https://i.stack.imgur.com/ltPQu.png

$("div").each(function() { var CHARS = $(this).text().split(''); $(this).html(""); $.each(CHARS, function(index, char) { var canvas = $("") .css("width", "40px") .css("height", "40px") .get(0); $("div").append(canvas); var ctx = canvas.getContext("2d"); var gradient = ctx.createLinearGradient(0, 0, 130, 0); gradient.addColorStop("0", "blue"); gradient.addColorStop("0.5", "blue"); gradient.addColorStop("0.51", "red"); gradient.addColorStop("1.0", "red"); ctx.font = '130pt Calibri'; ctx.fillStyle = gradient; ctx.fillText(char, 10, 130); }); });

Example Text


BTW, you can use 0.5 for the red colour stop, too.
R
Ruskin

If you are interested in this, then Lucas Bebber's Glitch is a very similar and super cool effect:

https://i.stack.imgur.com/63fkI.gif

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


A
Al Foиce ѫ

Closest I can get:

$(function(){ $('span').width($('span').width()/2); $('span:nth-child(2)').css('text-indent', -$('span').width()); }); body{ font-family: arial; } span{ display: inline-block; overflow: hidden; } span:nth-child(2){ color: red; } XX

Demo: http://jsfiddle.net/9wxfY/2/

Heres a version that just uses one span: http://jsfiddle.net/9wxfY/4/


$('span').width() just returns the width of the first span it finds; it'd have to be something you did for each pair. Which gives me an idea ...
This is quite similar to epascarello's example found at jsfiddle.net/9WWsd . As I told him, Your example is a step in the right direction, but, it would be a nightmare to use on a larger scale.
@MathewMacLean, I didn't see that. Why would it be a nightmare? How about this: jsfiddle.net/9wxfY/4
When you go to implement more than one character it causes problems.
Encase you're interested, I had a little look at optimising the JS in your second example: jsfiddle.net/9wxfY/20
T
TylerH

https://i.stack.imgur.com/RftEr.png

I just played with @Arbel's solution:

var textToHalfStyle = $('.textToHalfStyle').text(); var textToHalfStyleChars = textToHalfStyle.split(''); $('.textToHalfStyle').html(''); $.each(textToHalfStyleChars, function(i,v){ $('.textToHalfStyle').append('' + v + ''); }); body{ background-color: black; } .textToHalfStyle{ display:block; margin: 200px 0 0 0; text-align:center; } .halfStyle { font-family: 'Libre Baskerville', serif; position:relative; display:inline-block; width:1; font-size:70px; color: black; overflow:hidden; white-space: pre; text-shadow: 1px 2px 0 white; } .halfStyle:before { display:block; z-index:1; position:absolute; top:0; width: 50%; content: attr(data-content); /* dynamic content for the pseudo element */ overflow:hidden; color: white; } Dr. Jekyll and M. Hide


Just played with @Arbel solution What are the differences with Arbel's solution? It's hard to see if you only copy-pasted some code or improved it.
A
Al Foиce ѫ

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 { position: relative; color: rgba(50,50,200,0.5); } span:before { content: attr(data-char); position: absolute; width: 50%; overflow: hidden; color: rgb(50,50,200); } X


A
Adjit

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(){ var $hc = $('.half-color'); var str = $hc.text(); $hc.html(""); var i = 0; var chars; var dupText; while(i < str.length){ chars = str[i]; if(chars == " ") chars = " "; dupText = "" + chars + ""; var firstHalf = $(dupText); var secondHalf = $(dupText); $hc.append(firstHalf) $hc.append(secondHalf) var width = firstHalf.width()/2; firstHalf.width(width); secondHalf.css('text-indent', -width); i++; } }); .half-color span{ font-size: 2em; display: inline-block; overflow: hidden; } .half-color span:nth-child(even){ color: red; }

This is a sentence


T
TylerH

A nice 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);
   background-clip: text;
   -webkit-text-fill-color: transparent;
}

S
Sam Tremaine
.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.


k
kshetline

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:

https://i.stack.imgur.com/IjKUY.png

p.char { position: relative; display: inline-block; font-size: 60px; color: red; } p.char:before { position: absolute; content: attr(char); width: 50%; overflow: hidden; color: black; }

S

t

a

c

k

o

v

e

r

f

l

o

w


R
Ricardo Zea

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);
  }
}

S
Sleek Geek

This can be achieved with just CSS :before selector and content property value.

.halfed, .halfed1 { float: left; } .halfed, .halfed1 { font-family: arial; font-size: 300px; font-weight: bolder; width: 200px; height: 300px; position: relative; /* To help hold the content value within */ overflow: hidden; color: #000; } .halfed:before, .halfed1:before { width: 50%; /* How much we'd like to show */ overflow: hidden; /* Hide what goes beyond our dimension */ content: 'X'; /* Halfed character */ height: 100%; position: absolute; color: #28507D; } /* For Horizontal cut off */ .halfed1:before { width: 100%; height: 55%; }

X
X

>> See on jsFiddle


S
Salman A

You can also do it using SVG, if you wish:

var title = document.querySelector('h1'), text = title.innerHTML, svgTemplate = document.querySelector('svg'), charStyle = svgTemplate.querySelector('#text'); svgTemplate.style.display = 'block'; var space = 0; for (var i = 0; i < text.length; i++) { var x = charStyle.cloneNode(); x.textContent = text[i]; svgTemplate.appendChild(x); x.setAttribute('x', space); space += x.clientWidth || 15; } title.innerHTML = ''; title.appendChild(svgTemplate);

This is not a solution X

http://codepen.io/nicbell/pen/jGcbq


s
schmijos

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

https://i.stack.imgur.com/yQQha.png

body { text-align: center; margin: 0; } h1 { color: #111; font-family: arial; position: relative; font-family: 'Oswald', sans-serif; display: inline-block; font-size: 2.5em; } h1::after { content: attr(data-title-text); color: #e5554e; position: absolute; left: 0; top: 0; clip: rect(0, 1000px, 30px, 0); }

Display Text


m
mrReiha

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.

https://i.stack.imgur.com/yMu0s.png

But!

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>

https://i.stack.imgur.com/Zo4J9.png

Again, But!

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 )

https://i.stack.imgur.com/0zbUJ.gif

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...


J
Jose Rui Santos

https://i.stack.imgur.com/9Rtre.png

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; } Font-size:

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.

Vanilla JavaScript solution for all browsers

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); } #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%); } Font-size:

A Fallback Solution...For all browsers


t
the Hutt

All solutions work by splitting letters and wrapping them in <span>. We don't have to split letters in two cases:

If font is monospace.

If vertical layout is used.

div { font-size: 80px; font-weight: bolder; color: transparent; padding: 0; margin: 0; background: linear-gradient(90deg, rgb(34, 67, 143) 0% 50%, #409FBF 50%); background-clip: text; -webkit-background-clip: text; } .one { font-family: 'Nova Mono'; background-repeat: repeat-x; background-size: 45px; } .two { font-family: 'Gideon Roman'; writing-mode: vertical-lr; text-orientation: upright; letter-spacing: -35px; height: 500px; }

X-RAY Winter
Minty

Expected output, in case the fonts are not available:

https://i.stack.imgur.com/n4N1F.png

I know use of background-clip and gradient has been already demonstrated in other answers, just putting the cases where you don't have to split the letters.