Meta Stack Exchange is where users like you discuss bugs, features, and support issues that affect the software powering all 153 Stack Exchange communities.

What is meta?
Here's how it works:
  1. Any Stack Exchange user can ask a question
  2. The community provides support, votes on ideas, and reports bugs
  3. Your voice helps shape the way Stack Exchange operates

Especially on websites like Ask Different, <kbd>foo</kbd> is used a lot to markup keys on a keyboard correctly.

It would be super useful to have a simple Markdown syntax for it.

There’s already a syntax for inline code spans:

`x = 42;`

…which turns into:

<code>x = 42;</code>

For <kbd>, I’d like to suggest double backtick syntax:


…which would then be converted into:


Currently, the double backtick syntax has the same effect as the single backtick syntax — it just turns into an inline <code> span. So I’d say this change is backwards compatible.

Update: As Brock Adams pointed out in the comments, the double backtick syntax is already used for raw code snippets, so it’s probably not ideal to overload it. How about using the tilde, e.g. ~Shift~?

This would allow us to type keyboard keys much faster. Thanks for your consideration.

share|improve this question
Can't use a double back-tick; It would totally break displaying a code-formatted slash (\). – Awesome Poodles Apr 23 '12 at 9:24
The <kbd> tag used to work, didn't it? Like <kbd>F1</kbd> – Henk Holterman Apr 23 '12 at 9:24
@HenkHolterman, it still works. He just doesn't want to have to type it all out. – Awesome Poodles Apr 23 '12 at 9:25
Why not give yourself a button that does it? Shouldn't be hard to script... – Manishearth Apr 23 '12 at 9:26
@Manishearth A Markdown syntax would be much faster. I’m a keyboard user; I don’t like pressing buttons. – Mathias Bynens Apr 23 '12 at 9:27
@BrockAdams - not in comments (anymore?) – Henk Holterman Apr 23 '12 at 9:27
@MathiasBynens, you can use script to give keyboard shortcuts to things, too (or even implement your own mark-up interpreter). – Awesome Poodles Apr 23 '12 at 9:28
@HenkHolterman, <kbd> never did work in comments. They've deliberately kept most markup out of those. – Awesome Poodles Apr 23 '12 at 9:30
Why the downvote? – Mathias Bynens Apr 23 '12 at 9:46
@MathiasBynens Voting is different on meta – Manishearth Apr 23 '12 at 9:52
@Manishearth How can anyone “disagree” with a feature request like this? If you don’t need it, it won’t bother you either — just don’t use it then. – Mathias Bynens Apr 23 '12 at 9:55
Technically, any addition to markdown is a breaking change - after all, anyone could have used ~Foo~ at any point. Is this really common enough to need markdown changes? As a side note, editor changes (i.e. some keypress like the existing Ctrl+B for bold etc) might be less contentious... – Marc Gravell Apr 23 '12 at 9:55
@mathias. What Marc said. I'm open to writing a userscript for you guys, though it may take a while. – Manishearth Apr 23 '12 at 10:01
@MarcGravell What would be an acceptable example of using ~Foo~ in text (not code!) where removing the ~ would “break” it, i.e. make it unreadable? – Mathias Bynens Apr 23 '12 at 10:44
@MathiasBynens how about the one in your question? all of a sudden, the meaning has changed... (ok, in reality we wouldn't apply inside ` sections) – Marc Gravell Apr 23 '12 at 10:52

I went ahead and made a userscript for this. See "kbd markup shortcuts" at stackapps. This script adds both a button and a keyboard shortcut (AltK) for adding <kbd></kbd> tags.

Read more about it at stackapps, or install it.

Copy of the latest source:

// ==UserScript==
// @name            _Add kbd shortcut
// @namespace       StackExchange
// @description     Adds a button and a keyboard shortcut (Alt-K) to add <kbd> tags.
// @match           http://**
// @match           http://**
// @match           http://**
// @match           http://**
// @match           http://**
// @match           http://**
// @match           http://**
// ==/UserScript==

function AddKbdShortcuts ($) {
    $("textarea.wmd-input").each (AddKbdButtonAsNeeded);

    //.on() not working right!!
    $("textarea.wmd-input").live ("focus",   AddKbdButtonAsNeeded);
    $("textarea.wmd-input").live ("keydown", InsertKbdTagByKeypress);
    $("li.wmd-kbd-button") .live ("click",   InsertKbdTagByClick);

    function AddKbdButtonAsNeeded () {
        var jThis   = $(this);
        if ( ! ("hasKbdBttn") ) {
            //--- Find the button bar and add our button.
            var btnBar  = jThis.prevAll ("div.wmd-button-bar");
            if (btnBar.length) {
                //--- The button bar takes a while to AJAX-in.
                var bbListTimer = setInterval ( function() {
                        var bbList  = btnBar.find ("ul.wmd-button-row");
                        if (bbList.length) {
                            clearInterval (bbListTimer);
                            bbList.append (
                                  '<li class="wmd-button wmd-kbd-button" '
                                + 'title="Keyboard tag &lt;kbd&gt; Alt+K" '
                                + 'style="left: 380px;">'
                                + '<span style="background: white;">[kbd]</span></li>'
                   ("hasKbdBttn", true);

    function InsertKbdTagByKeypress (zEvent) {
        //--- On Alt-K, insert the <kbd> set. Ignore all other keys.
        if (zEvent.altKey  &&  zEvent.which == 75) {
            InsertKbdTag (this);
            return false;
        return true;

    function InsertKbdTagByClick (zEvent) {
        //--- From the clicked button, find the matching textarea.
        var targArea    = $(this).parents ("div.wmd-button-bar")
                        .nextAll ("textarea.wmd-input");

        InsertKbdTag (targArea[0]);
        targArea.focus ();
        try {
            //--- This is a utility function that SE currently provides on its pages.
            StackExchange.MarkdownEditor.refreshAllPreviews ();
        catch (e) {
            console.warn ("***Userscript error: refreshAllPreviews() is no longer defined!");

    function InsertKbdTag (node) {
        //--- Wrap selected text or insert at curser.
        var tagLength       = 5;    // Tag is: "<kbd>"
        var oldText         = node.value || node.textContent;
        var newText;
        var iTargetStart    = node.selectionStart;
        var iTargetEnd      = node.selectionEnd;
        var selectedText    = oldText.slice (iTargetStart, iTargetEnd);
        var possWrappedTxt;
        try {
            //--- Lazyman's overrun checking...
            possWrappedTxt  = oldText.slice (
                                iTargetStart - tagLength,
                                iTargetEnd   + tagLength + 1
        catch (e) {
            possWrappedTxt  = "Text can't be wrapped, cause we overran the string.";

        /*--- Is the current selection wrapped?  If so, just unwrap it.
            This works the same way as SE's bold, italic, code, etc...
            "]text["                --> "<kbd>]text[</kbd>"
            "<kbd>]text[</kbd>"     --> "]text["
            "]<kbd>text</kbd>["     --> "<kbd>]<kbd>text</kbd>[</kbd>"

            Except that:
            "]["                    --> "<kbd>][</kbd>"
            "<kbd>][</kbd>"         --> "]["
            with no placeholder text.
            Note that `]` and `[` denote the selected text here.
        if (possWrappedTxt  &&
            selectedText    == possWrappedTxt.replace (/^<kbd>((?:.|\n|\r)*)<\/kbd>$/, "$1")
        ) {
            iTargetStart   -= tagLength;
            iTargetEnd     += tagLength + 1;
            newText         = oldText.slice (0, iTargetStart)
                            + selectedText + oldText.slice (iTargetEnd)
            iTargetEnd      = iTargetStart + selectedText.length;
        else {
            /*--- Here we will wrap the selection in our tags, but there is one extra
                condition.  We don't want to wrap leading or trailing whitespace.
            var trimSelctd  = selectedText.match (/^(\s*)(\S?(?:.|\n|\r)*\S)(\s*)$/)
                            || ["", "", "", ""]
            if (trimSelctd.length != 4) {
                console.warn ("***Userscript error: unexpected failure of whitespace RE.");
            else {
                newText         = trimSelctd[1]     //-- Leading whitespace, if any.
                                + '<kbd>' + trimSelctd[2] + '</kbd>'
                                + trimSelctd[3]     //-- Trailing whitespace, if any.
                newText         = oldText.slice (0, iTargetStart)
                                + newText + oldText.slice (iTargetEnd)
                iTargetStart   += tagLength + trimSelctd[1].length;
                iTargetEnd     += tagLength - trimSelctd[3].length;

        //console.log (newText);
        node.value          = newText;
        //--- After using spelling corrector, this gets buggered, hence the multiple sets.
        node.textContent    = newText;

        //--- Have to reset the selection, since we overwrote the text.
        node.selectionStart = iTargetStart;
        node.selectionEnd   = iTargetEnd;

withPages_jQuery (AddKbdShortcuts);

function withPages_jQuery (NAMED_FunctionToRun) {
    //--- Use named functions for clarity and debugging...
    var funcText        = NAMED_FunctionToRun.toString ();
    var funcName        = funcText.replace (/^function\s+(\w+)\s*\((.|\n|\r)+$/, "$1");
    var script          = document.createElement ("script");
    script.textContent  = funcText + "\n\n";
    script.textContent += 'jQuery(document).ready( function () {' + funcName + '(jQuery);} );';
    document.body.appendChild (script);
share|improve this answer
No fair I was going to do that!! – Manishearth Apr 24 '12 at 5:57
@Manishearth: You can still (1) Upvote it (here and there), and (2) work on the "Known limitations, possible future enhancements" I listed. (^_^) Plenty of script for the both of us! – Awesome Poodles Apr 24 '12 at 6:04
Done that :). Will take a look at it.. Though, being an amateur programmer, I don't think I can help much. – Manishearth Apr 24 '12 at 6:07
Managed to add a fix anyway :) – Manishearth Apr 24 '12 at 6:29
@Manishearth, thanks! – Awesome Poodles Apr 24 '12 at 6:49

For Pentadactyl users wanting a simple macro to insert the kbd tags and move the cursor, here's what you need in you .pentadactylrc to add an Alt+k binding on Stack Exchange sites (extend site list as necessary).

group stackexchange,,
map -modes insert <A-k> <S-<>kbd<S->><S-<>/kbd<S->><Left><Left><Left><Left><Left><Left>
group user
share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .