Pen Settings



CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource


Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource


Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.


Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.


                <hr id="cyo_debug">

[Start Over]:#cyo.begin:retry

<hr id="cyo.get_the_code:yes">

# You can find the code...

Right now, I've put the code in [a gist][gist].

I'll probably pull it all into a proper repository soon, but you can also just check out this pen for all the juicy details.

[But how does it work?][how_does_it_work]

[Thanks, I'm all done!][all_done]

<hr id="cyo.about_buttons:ok">

# The Buttons

The buttons were the funnest thing to work on. Again, with the goal of minimal impact to Markdown syntax, the buttons work by matching _parts_ of the fragment you are trying to navigate to.

For example

[This link will be blue](#cyo.this-is-where-i-wanna-go:ok)

The button styles will only apply if the `href` of the link is prefixed with `#cyo`, then its icon and color are determined by the suffix.


(Warning! clicking these will take you back to the start)

[No Suffix](#cyo.nowhere)
[`:ok` suffix](#cyo.nowhere:ok)
[`:yes` suffix](#cyo.nowhere:yes)
[`:no` suffix](#cyo.nowhere:no)
[`:warn` suffix](#cyo.nowhere:warn)
[`:info` suffix](#cyo.nowhere:info)
[`:retry` suffix](#cyo.nowhere:retry)
[`:q` suffix](#cyo.nowhere:q)

A _total_ hack, but a fun one!

Okay, what now?

[The Core CSS][about_CSS]
[Markdown Hacks][about_MD]

[I got it...][all_done]

<hr id="cyo.about_md:ok">

# Hacking Markdown

This also works by 'hacking' in some extensions into the existing markdown format. Namely by specifying links with anchors:

[Lets go!](

While, this _is_ a basic feature of Markdown, we leverage almost more like a `class`, matching parts of the `id` to reuse functionality. The fragment caries more information than just where to go on the page.

For Markdown compilers that don't support adding ID's to elements, then you have to fall back to HTML for defining sections:

<hr id="cyo.step1">
# Part 1
Welcome to step one. Thats it!

[Start Over!](#cyo.begin)

<hr id="cyo.begin">
#Hello there
Ready to get started?

[Goto Step 1](#cyo.step1)

Other syntaxes of Markdown allow for built-in id creation.

I origionally started with a more semantic HTML approach using the `<section>` element, which I liked. However, it became problematic &ndash; and frankly ugly &ndash; when trying to mix that style with the limited grammar of Markdown. I enjoy the _speed_ of writing Markdown too much to sacrifice that. But perhaps I'll revisit that someday...

Any more questions?

[How does the CSS work?][about_CSS]
[But those buttons?][about_buttons]

[No thanks, I got it...][all_done]

<hr id="cyo.breakdown_css:ok">

# Taking the CSS rule-by-rule.

1. ```*[id^='cyo'], *[id^='cyo'] ~ * { display: none; }```
    Find any element whose `id` begins with `cyo` and all of thier siblings, and hide them.
1. ```*[id^='cyo']:last-of-type ~ * { display: block; }```

    Find the last element with an `id` that begins with `cyo` and make all of it's siblings displayed again.
    This is used to bootstrap a request that has no `#fragment` in the URL. You place your 'first' page, as the last section in the document. This rule gets 'overriden' by the next two rules.

1. ```*[id^='cyo']:target ~ * { display: block }```

    If there is a fragment, find all of its siblings and show them.
    But _oops_, now I've shown from the match to the bottom of the page!
1. `*[id^='cyo']:target ~ *[id^='cyo'],
      *[id^='cyo']:target ~ *[id^='cyo'] ~ * {
        display: none;
    From the target, find the _next_ element that has an id that starts with `cyo` and then hide it and all it's siblings. 

What do you want to know about now?

[The Markdown][about_MD]
[Those Custom Buttons][about_buttons]

[No thanks, I got it...][all_done]

<hr id="cyo.about_css:ok">

# NoJS? How?

The core of display engine requires only 7 lines of css:

*[id^='cyo'], *[id^='cyo'] ~ * { display: none; }
*[id^='cyo']:last-of-type ~ * { display: block; }
*[id^='cyo']:target ~ * { display: block }
*[id^='cyo']:target ~ *[id^='cyo'],
*[id^='cyo']:target ~ *[id^='cyo'] ~ * {
  display: none;

It uses `<hr>` elements with a special `id`, hidden within the document to designate "sections" of the document. Then it uses the document fragment to control the visibility of the section. 

It (ab)uses a few simple css features:

* Attribute begins with `[id^='']`
* The general sibling selector `~`
* The `:target` pseduo-class, which represents the unique element, if any, with an id matching the fragment identifier of the URI of the document. [source](

Maybe a rule by rule breakdown would help?

[Rule By Rule Breakdown][breakdown_css]

Or we can talk about another topic.

[Markdown Extension][about_MD]
[Custom Buttons][about_buttons]

[No thanks, I got it...][all_done]

<hr id="cyo.how_does_it_work:q">

# I'm glad you asked!

So, in working on this, I had a couple of interesting constraints. I wanted it to be:

* A CSS only solution &ndash; no-JS.
* Play nicely with Markdown.
* Allow custom links/buttons styles, again without breaking MD

Which do you want to learn more about?

[The CSS][about_CSS]
[Markdown Hacks][about_MD]
[Custom Buttons][about_buttons]

[I got it...][all_done]

<hr id="cyo.abort:no">

# Well, shucks!

I'm sorry you feel that way! But thanks for taking the time to check this out anyway!

[Start Over]

<hr id="cyo.all_done:yes">

# Thanks for checking out this experiment.

Thanks for trying out my little experiment in storytelling and tutorials. Feel free to hit me up with any questions, comments or improvements anytime!

Cheers and _keep coding!_

[Start Over][Start Over]

Oh wait, did you forget to get the code?

[Get the code][get_the_code]

<hr id="cyo.step1:yes">

# Great, so have I!

That's why I've been working on this:

A ["Choose Your Own Adventure"]( style markdown extension that helps simplify the construction and distrubution of branching style stories and tutorials. 

It's a CSS-only extension &ndash; doesn't use any JavaScript &ndash; and tries to stay out of your way as much as possible but still add a sliver of interactivity to traditional markdown documents.

It should also play nice with almost every markdown compiler that supports adding custom CSS, and shouldn't interfere with existing themes.

[Cool, where can I get the code][get_the_code]
[Extension? What do you mean?][how_does_it_work]
[That's lame][lame]

<hr id="cyo_begin">

# Choose Your Own Tutorial...

I've been a developer and a teacher for quite a few years now, and while I love writing tutorials, I sometimes miss the ability of working directly with a co-worker or student and really help them understand a problem.

Have you ever wanted to write a tutorial, but wanted to customize the learning experience for multiple skill levels?

Have you ever wanted to explore an idea collaboratively, and didn't want to feel like you were talking _at_ somebody?





                // CYO logic
*[id^='cyo'], *[id^='cyo'] ~ * { display: none; }
*[id^='cyo']:last-of-type ~ * { display: block; }
*[id^='cyo']:target ~ * { display: block }
*[id^='cyo']:target ~ *[id^='cyo'],
*[id^='cyo']:target ~ *[id^='cyo'] ~ * {
  display: none;
*[id='cyo_debug']:target ~ * { display: block !important; }
*[id='cyo_debug']:target ~ hr[id] { display: none; }

@import '//';

// Link Helpers 
a[href^='#cyo'] { 

  // Button Colors
  @clr-default: #444;
  @clr-primary: #2A8FBD;
  @clr-success: #7FAF1B;
  @clr-warning: #FBB829;
  @clr-danger:  #F02311;
  @clr-info:    #6CDFEA;  
  @push: 3px;
  @pull: 2px;
  font-size: 0.9em;
  border-style: solid;
  border-width: 1px;
  border-bottom-width: @push;
  display: inline-block;
  margin-bottom: @push;
  text-align: center;
  padding: 0.5em 1em 0.5em 0.35em;
  border-radius: 0.4em;
  text-decoration: none;
  margin-top: 0;
  vertical-align: bottom;
    border-color 0.33s,
    background-color 0.33s, 
    color 0.133s, 
    border-bottom-width 0.133s,
    margin-top 0.133s;
  &:hover {
      linear-gradient(0deg, transparent, fadeout(white, 70%));
    margin-top: -(@pull);
    border-bottom-width: @push + @pull;
  &:active {
    margin-top: @push;
    border-bottom-width: 0;
  .colorize(@clr) { 
    color: @clr;
    border-color: transparent;
    &:hover, &:focus { 
      color: lighten(@clr, 50%);
      border-top-color: lighten(@clr, 15%);
      border-bottom-color: darken(@clr, 20%);
      background-color: @clr;

  &[href$=':ok'], &[href$='.ok'] { .colorize(@clr-primary); }
  &[href$=':yes'], &[href$='.yes'] { .colorize(@clr-success); }
  &[href$=':no'], &[href$='.no'] { .colorize(@clr-danger); }
  &[href$=':warn'], &[href$='.warn'] { .colorize(@clr-warning); }
  &[href$=':info'], &[href$='.info'] { .colorize(@clr-info); }
  &[href$=':q'], &[href$='.q'] { .colorize(@clr-primary); }

  &::before {
    font-family: 'FontAwesome';
    font: normal normal normal 14px/1 FontAwesome;
    width: 24px;
  &:hover::before {
    font-size: 1.5em;
    line-height: 0.75em;
    vertical-align: -15%;
  &::before { content: "\f054"; }
  &[href$=':ok']::before, &[href$='.ok']::before { content: "\f054"; }
  &[href$=':yes']::before, &[href$='.yes']::before { content: "\f00c"; }
  &[href$=':no']::before, &[href$='.no']::before { content: "\f00d"; }
  &[href$=':warn']::before, &[href$='.warn']::before { content: "\f0e7"; }
  &[href$=':info']::before, &[href$='.info']::before { content: '\f12a'; }
  &[href$=':q']::before, &[href$='.q']::before { content: '\f128'; }
  &[href$=':retry']::before, &[href$='.retry']::before { content: '\f021'; }

h1,h2,h3,h4,h5,h6 { font-weight: 700; letter-spacing: -1pt; }
@import url(,400,700);
:root { 
  font-family: 'Open Sans', sans-serif;
  padding: 2em;
  min-height: 100vh;
    radial-gradient(center farthest-corner, transparent, fade(black, 15%));
body {
  width: 75vw;
  min-width: 500px;
  margin: 0 auto;

code, a > code {
  background-color: fade(black, 15%);
  padding: 0.25ch 1ch;
  border-radius: 0.5ch;
  border: 1px solid fade(black, 20%);

li { margin-top: 1ex; margin-bottom: 1ex; }

*[id^='cyo']:target { display: block; position: absolute; top: 0; width: 0; height: 0; visibility: hidden; }

pre>code {
  display: block;
  padding: 1ch 2ch;