Note: This project has been changed a lot, and I need to rewrite this post. Until then, I suggest visiting my github page below
Updated: 11.17.2012 — Created github repo for cssterm package
Don’t care about the background, and just want to see how to use it?
I know that I will be sharing a lot of linux tips, fixes, and information on here, since it is one of my primary passions and something I deal with on a daily basis. So, I’ve been wondering how to best display linux commands in a meaningful and attractive way on a blog. Over the years I’ve seen a lot of good methods for this used in tech books and random websites, but now that the task was set upon me I couldn’t think of a single one. So I stalled while I thought of what to do.
After a lot of googling (to make sure there weren’t any clear suggestions out there; I didn’t find much with my search terms at least) and soul searching, I decided that I wanted to steal some of the clever CSS that is out there and morph it a bit to my purposes.
As I said, I wanted it to be very clear to the reader that the text they were reading were either commands that should be run, or output from having run a command. There’s an awesome project that you are likely familiar with (and if not, scroll down a bit and you will be) called
SyntaxHighlighter that does this for code. JavaScript parses the webpage and reformats it based upon what language you are discussing, and it is very useful. Sadly, nothing like this seems to exist for my purposes, so I figured I would put something together; the goal being to make it as easy as possible for others to follow suit.
The Requirements
- Pure CSS if possible. I would rather avoid calling images (which in turn means I don’t have to see what the common thread is for image hosting amongst the various blog options. See? I’m always thinking of you!)
- Easy syntax. I won’t remember the 15 layers of divs required, so I need some code to do that for me. At this point, I’ve accepted that there is going to be javascript involved.
- I’m not a great coder, so it should be based off an existing, common library. Some of it is going to have to be mine, but why limit myself to my own talents (and follies) if it isn’t necessary.
Stealing from the community
We all post this stuff because we’re trying to make the web more useful, right? So why start on my own when I can get some ideas from other people and avoid some of the grunt work. I like to consider this a private brainstorming session between me and the rest of the world.
The first thing I searched for was some clever CSS. That led me to
CSS Deck which seems to have a lot of really amazing ideas and examples. On there I actually found several examples that mimicked the OS X terminal, but none for linux. But hey, close enough right? At least, close enough for me to use what they have and just tweak it a bit.. The one that I liked most was
the ‘pure css osx terminal’.
A few sample tests on my wordpress page and I could see that this was going to work just fine. But, as I feared, it involved a lot of nested div tags. This is perfectly understandable and acceptable for a random part of your website, but not for daily use. So, we’re gonna have to use some javascript.
My mind immediately went to
jQuery. I’ve used it a few times on different websites and it is not only a pleasure to work with, but a very powerful tool. At least, for a man of my ability. I also seemed to recall a function within it that was used to look for tags in the html, and replace it with more complex (and dynamic) code. This worked out quite well, and I’m happy with having jquery as a requirement given how widespread it is amongst sites to begin with.
The Methods
I quickly realized that this isn’t a one-size-fits-all problem. You need to be able to easily note how a line should be displayed without doing more formatting on your own. My first considerations were the differences between “commands”, “comments”, and “stdout”. So, a bit more scripting and we were able to accomplish this.
Now the following changes are automatically made in the javascript based on the first character of the line:
- # – line should be formatted as a ‘command’
- ! – line should be formatted as a ‘comment’
- (nothing) – line should be formatted as standard output
The next thing that occurred to me is that sometimes you want to be able to signify a command in the middle of a paragraph, without creating an entire terminal window, but still matching the overall theme. A few quick updates to the CSS, and then we had it.
This…
Lorem ipsum dolor sit amet, consectetur. Donec euismod, risus ac
auctor posuere, enim dolor fermentum libero, ut sagittis dui neque
vel magna. Fusce odio tellus,<div class="terminal-oneliner">ls -al
/usr/local/etc/zones/rev/</div> faucibus sit amet cursus vitae,
cursus ac ligula
Becomes this…
Lorem ipsum dolor sit amet, consectetur. Donec euismod, risus ac auctor posuere, enim dolor fermentum libero, ut sagittis dui neque vel magna. Fusce odio tellus,
ls -al /usr/local/etc/zones/rev/
faucibus sit amet cursus vitae, cursus ac ligula
Conclusion
All in all, this worked out quite well. It was a short, fun project to get under my belt and hopefully is something I will use in many posts in the future. With any luck, it will help some of you out there too!
I am certain I will continue to tweak this more as I figure out exactly what my needs are and encounter situations where it doesn’t work quite right, but I would also love to hear from you if you have any suggestions or concerns about the methodology.
Off the top of my head, the only initial concern I have is regarding SEO. I’m wondering if the way that I have this placed inside of a div will affect search engines crawling the page and being confused by the lack of line breaks. I could alleviate this by putting it inside of pre tags instead, but I’m not sure that is necessary. If it is, I’ll be sure to report back.
I don’t care about the process, give me the code!
Demo
This…
<!-- you MUST include jquery for the rest of the javascript to work -->
<script src="http://code.jquery.com/jquery-latest.js"></script>
<div class="terminal">
! use the 'uptime' command to check current basic system health
# uptime
14:16:34 up 90 days, 9:26, 8 users, load average: 0.94, 0.88, 0.83
! if you need to find out the kernel information, you want 'uname'
# uname -a
Linux vertigo 2.6.32-5-686 #1 SMP Mon Jan 16 16:04:25 UTC 2012 i686 GNU/Linux
</div>
Becomes this…
! use the ‘uptime’ command to check current basic system health
# uptime
14:16:34 up 90 days, 9:26, 8 users, load average: 0.94, 0.88, 0.83
! if you need to find out the kernel information, you want ‘uname’
# uname -a
Linux vertigo 2.6.32-5-686 #1 SMP Mon Jan 16 16:04:25 UTC 2012 i686 GNU/Linux
CSS
/* FINDER WINDOW */
.terminal-oneliner{
color: #63de00!important;
padding: 0 4px 0 4px!important;
margin-top:4px!important;
margin-left: auto!important;
margin-right: auto!important;
margin-bottom:4px!important;
border-radius: 5px!important;
-webkit-box-shadow: 0px 0px 10px rgba(0,0,0,0.75)!important;
-moz-box-shadow: 0px 0px 10px rgba(0,0,0,0.75)!important;
/*-webkit-transition:all 0.5s; */
overflow: hidden!important;
display: inline !important;
border:1px solid #6e8ba1!important;
background: #000!important;
}
#terminal-window{
width: 98%;
margin-top:10px;
margin-left: auto;
margin-right: auto;
margin-bottom:10px;
min-width: 500px;
background: #fff;
border-radius: 5px;
-webkit-box-shadow: 0px 0px 20px rgba(0,0,0,0.75);
-moz-box-shadow: 0px 0px 20px rgba(0,0,0,0.75);
/*-webkit-transition:all 0.5s; */
overflow: hidden;
}
/* TOP BAR */
#terminal-toolbar{
width: 100%;
height: 25px;
background: grey;
border-radius:5px 5px 0 0;
background: #cfcfcf; /* Old browsers */
background: -moz-linear-gradient(top, #cfcfcf 0%, #a8a8a8 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#cfcfcf), color-stop(100%,#a8a8a8)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #cfcfcf 0%,#a8a8a8 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #cfcfcf 0%,#a8a8a8 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #cfcfcf 0%,#a8a8a8 100%); /* IE10+ */
background: linear-gradient(top, #cfcfcf 0%,#a8a8a8 100%); /* W3C */
-webkit-box-shadow:0px 1px 0px rgba(255,255,255,0.5) inset,0px 1px 0px #515151;
-moz-box-shadow:0px 1px 0px rgba(255,255,255,0.5) inset,0px 1px 0px #515151;
box-shadow:0px 1px 0px rgba(255,255,255,0.5) inset,0px 1px 0px #515151;
}
#terminal-toolbar .top{
float: left;
width: 100%;
height: 23px;
}
#terminal-toolbar .bottom{
float: left;
width: 100%;
height: 30px;
}
/*-----TRAFFIC LIGHTS-----*/
#terminal-toolbar #terminal-lights{
float: left;
position:relative;
top:4px;
left:7px;
}
.terminal-light{
float:left;
width:14px;
height:14px;
border-radius:14px;
-webkit-box-shadow:0px 1px 0px rgba(255,255,255,0.5),0px 0px 3px #000 inset;
-moz-box-shadow:0px 1px 0px rgba(255,255,255,0.5),0px 0px 3px #000 inset;
box-shadow:0px 1px 0px rgba(255,255,255,0.5),0px 0px 3px #000 inset;
overflow: hidden;
}
#terminal-lights:hover .glyph{
opacity: 1;
cursor:default;
}
.terminal-light .terminal-shine{
width: 4px;
height:3px;
border-radius:10px;
/*background-image: -webkit-gradient(radial, center center, 0, center center, 3, from(rgba(255,255,255,1)), to(rgba(255,255,255,0)));*/
background: -moz-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ */
background-image: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */
background: -o-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Opera 12+ */
background: -ms-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ */
background: radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* W3C */
}
.terminal-light .terminal-glow{
width:14px;
height:8px;
background-image: -webkit-gradient(radial, center bottom, 0, center center, 5, from(rgba(255,255,255,0.75)), to(rgba(255,255,255,0)));
background: 0px 0px -moz-radial-gradient(bottom, cover, rgba(255,255,255,0.70) 0%, rgba(255,255,255,0) 80%); /* FF3.6+ */
}
/*--RED--*/
.terminal-red{
background: #f41b16; /* Old browsers */
background: -moz-linear-gradient(top, #f41b16 0%, #fc7471 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f41b16), color-stop(100%,#fc7471)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f41b16 0%,#fc7471 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f41b16 0%,#fc7471 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #f41b16 0%,#fc7471 100%); /* IE10+ */
background: linear-gradient(top, #f41b16 0%,#fc7471 100%); /* W3C */
}
.terminal-red:active{
background: #972f2e; /* Old browsers */
background: -moz-linear-gradient(top, #972f2e 0%, #fc7471 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#972f2e), color-stop(100%,#fc7471)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #972f2e 0%,#fc7471 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #972f2e 0%,#fc7471 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #972f2e 0%,#fc7471 100%); /* IE10+ */
background: linear-gradient(top, #972f2e 0%,#fc7471 100%); /* W3C */
}
.terminal-red .terminal-shine{
position: relative;
top: -23px;
left: 5px;
}
.terminal-red .terminal-glow{
position: relative;
top: -22px;
}
.terminal-red .terminal-glyph{
position: relative;
top: -6px;
left: 3px;
font-size: 14px;
font-weight: bold;
color: #9b3a36;
z-index: 50;
opacity: 0;
}
/*--YELLOW--*/
.terminal-yellow{
background: #f4a316; /* Old browsers */
background: -moz-linear-gradient(left, #f4a316 0%, #fcc371 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, right top, color-stop(0%,#f4a316), color-stop(100%,#fcc371)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(left, #f4a316 0%,#fcc371 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(left, #f4a316 0%,#fcc371 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(left, #f4a316 0%,#fcc371 100%); /* IE10+ */
background: linear-gradient(left, #f4a316 0%,#fcc371 100%); /* W3C */
margin:0px 7px;
}
.terminal-yellow:active{
background: #ae4f1e; /* Old browsers */
background: -moz-linear-gradient(top, #ae4f1e 0%, #fcc371 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ae4f1e), color-stop(100%,#fcc371)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #ae4f1e 0%,#fcc371 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #ae4f1e 0%,#fcc371 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #ae4f1e 0%,#fcc371 100%); /* IE10+ */
background: linear-gradient(top, #ae4f1e 0%,#fcc371 100%); /* W3C */
}
.terminal-yellow .terminal-shine{
position: relative;
top: -23px;
left: 5px;
}
.terminal-yellow .terminal-glow{
position: relative;
top: -22px;
}
.terminal-yellow .terminal-glyph{
position: relative;
top: -7px;
left: 4px;
font-size: 24px;
color: #854322;
z-index: 50;
opacity: 0;
-webkit-transform: scaleY(1.5) scaleX(1.3);
}
/*--GREEN--*/
.terminal-green{
background: #4cae2e; /* Old browsers */
background: -moz-linear-gradient(top, #4cae2e 0%, #dafc71 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4cae2e), color-stop(100%,#dafc71)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4cae2e 0%,#dafc71 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4cae2e 0%,#dafc71 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #4cae2e 0%,#dafc71 100%); /* IE10+ */
background: linear-gradient(top, #4cae2e 0%,#dafc71 100%); /* W3C */
}
.terminal-green:active{
background: #48752b; /* Old browsers */
background: -moz-linear-gradient(top, #48752b 0%, #dafc71 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#48752b), color-stop(100%,#dafc71)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #48752b 0%,#dafc71 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #48752b 0%,#dafc71 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #48752b 0%,#dafc71 100%); /* IE10+ */
background: linear-gradient(top, #48752b 0%,#dafc71 100%); /* W3C */
}
.terminal-green .terminal-shine{
position: relative;
top: -22px;
left: 5px;
}
.terminal-green .terminal-glow{
position: relative;
top: -22px;
}
.terminal-green .terminal-glyph{
position: relative;
top: -6px;
left: 3px;
font-size: 14px;
font-weight: bold;
color: #25571d;
z-index: 50;
opacity: 0;
}
/* Horrible to do this for firefox */
@-moz-document url-prefix() {
.terminal-red .terminal-glyph {
position: relative;
top: -4px;
}
.terminal-yellow .terminal-glyph {
top: -4px;
left: 3px;
}
.terminal-green .terminal-glyph{
position: relative;
top: -4px;
}
}
/*-----TITLE-----*/
#terminal-title{
float: left;
position: relative;
top:4px;
width:40%;
left: 30%;
font-family: "Myriad Pro", sans-serif;
color: black;
font-size: 14px;
text-shadow: 0px 1px 0px rgba(255,255,255,0.5);
line-height: 14px;
}
.terminal-body{
width: 14px;
height: 10px;
border:1px solid #6e8ba1;
background: #b8cfe0; /* Old browsers */
background: -moz-linear-gradient(top, #b8cfe0 0%, #86adc8 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b8cfe0), color-stop(100%,#86adc8)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #b8cfe0 0%,#86adc8 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #b8cfe0 0%,#86adc8 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #b8cfe0 0%,#86adc8 100%); /* IE10+ */
background: linear-gradient(top, #b8cfe0 0%,#86adc8 100%); /* W3C */
z-index: -50;
-webkit-box-shadow:0px 1px 0px rgba(255,255,255,0.25) inset,0px 1px 0px rgba(0,0,0,0.2);
-moz-box-shadow:0px 1px 0px rgba(255,255,255,0.25) inset,0px 1px 0px rgba(0,0,0,0.2);
}
#terminal-body {
font-family: Andale Mono, monospace; line-height: 1em;
font-size:13px;
float: left;
width: 100%;
min-height:130px;
background:#000;
padding:10px;
line-height:1.5em;
}
#terminal-body p {
color: #63de00!important;
line-height: 200% !important;
}
@keyframes blink
{
0% { background:rgba(99,222,0,100); }
100% { background:rgba(99,222,0,0); }
}
@-webkit-keyframes blink {
0% { background:rgba(99,222,0,100); }
100% { background:rgba(99,222,0,0); }
}
@-moz-keyframes blink {
0% { background:rgba(99,222,0,100); }
100% { background:rgba(99,222,0,0); }
}
.terminal-cursor {
background:#63de00;
display:inline-block;
width:11px;
height:19px;
margin-bottom:-3px;
}
#terminal-body:hover .terminal-cursor {
-webkit-animation-name: blink;
-webkit-animation-duration: 1.5s;
-webkit-animation-iteration-count: infinite;
-moz-animation-name: blink;
-moz-animation-duration: 1.5s;
-moz-animation-iteration-count: infinite;
}
#terminal-body p::-webkit-selection {
background:#0b209e;
}
#terminal-body p::selection { background:#0b209e; }
#terminal-body p::-moz-selection { background:#0b209e; }
#terminal-body p {
margin-top:5px;
margin-bottom:5px;
font-size:11px;
}
.terminal-comment{
border:solid 1px #666666;
background:#333333;
color:#CCCCCC;
margin-left:2px;
width: 94%;
}
HTML
<!-- you MUST include jquery (somewhere on your site) for the rest of the javascript to work -->
<script src="http://code.jquery.com/jquery-latest.js"></script>
<div class="terminal">
! use the 'uptime' command to check current basic system health
# uptime
14:16:34 up 90 days, 9:26, 8 users, load average: 0.94, 0.88, 0.83
! if you need to find out the kernel information, you want 'uname'
# uname -a
Linux vertigo 2.6.32-5-686 #1 SMP Mon Jan 16 16:04:25 UTC 2012 i686 GNU/Linux
</div>
JAVASCRIPT
$(document).ready(function(){
var rstr = /^#/gi;
var cstr = /^!/gi;
var sstr = /^>/gi;
var rprompt = "[root@localhost]# ";
var comment = "<div class='terminal-comment'>";
var stdout = " ";
var termTop = "<div id=\"terminal-window\"> \
<div id=\"terminal-toolbar\"> \
<div class=\"terminal-top\"> \
<div id=\"terminal-lights\"> \
<div class=\"terminal-light terminal-red\"> \
<div class=\"terminal-glyph\">×</div> \
<div class=\"terminal-shine\"></div> \
<div class=\"terminal-glow\"></div> \
</div> \
<div class=\"terminal-light terminal-yellow\"> \
<div class=\"terminal-glyph\">-</div> \
<div class=\"terminal-shine\"></div> \
<div class=\"terminal-glow\"></div> \
</div> \
<div class=\"terminal-light terminal-green\"> \
<div class=\"terminal-glyph\">+</div> \
<div class=\"terminal-shine\"></div> \
<div class=\"terminal-glow\"></div> \
</div> \
</div> \
<div id=\"terminal-title\"> \
Terminal - ~: root@localhost: ~ \
</div> \
<div id=\"terminal-bubble\"> \
<div class=\"terminal-shine\"></div> \
<div class=\"terminal-glow\"></div> \
</div> \
</div> \
</div> \
<div id=\"terminal-body\"><p> \
";
var termBot = "</p> \
<div class=\"terminal-cursor\"></div> \
</div> \
</div> \
";
$(".terminal").each(function(){
var myContent = $(this).text();
var arrayContent = myContent.split('\n');
var newString = "";
jQuery.each(arrayContent, function() {
// make sure there's text to avoid blank spaces
if (this.length != 0 ) {
// is string a root command
if (this.charAt(0) == "#") {
newString += this.replace(rstr, rprompt).concat("<br>\n");
// is string a comment (don't forget to close that div)
} else if (this.charAt(0) == "!") {
newString += "</p>" + this.replace(cstr, comment).concat("</div>\n<p>");
// must be stdout
} else {
newString += stdout + this + "<br>\n";
}
}
});
$(this).replaceWith( termTop + newString + termBot);
});
});