In one of those “inaccessible CSS solution” moods. If you’re asking “should I do this?” you probably shouldn’t.
Oddly slow in latest Chrome on PC, works fine in FF.
A Pen by Jake Albaugh on CodePen.
In one of those “inaccessible CSS solution” moods. If you’re asking “should I do this?” you probably shouldn’t.
Oddly slow in latest Chrome on PC, works fine in FF.
A Pen by Jake Albaugh on CodePen.
| <a id=s1 title="Section 1 Anchor" class=s></a> | |
| <a id=s2 title="Section 2 Anchor" class=s></a> | |
| <a id=s3 title="Section 3 Anchor" class=s></a> | |
| <a id=s4 title="Section 4 Anchor" class=s></a> | |
| <a id=s5 title="Section 5 Anchor" class=s></a> | |
| <a id=s6 title="Section 6 Anchor" class=s></a> | |
| <a id=s7 title="Section 7 Anchor" class=s></a> | |
| <div id=progress></div> | |
| <div id=background></div> | |
| <nav class=prevnext role=presentation> | |
| <ul> | |
| <li class=p2><a href=#s1 accesskey=1 title="Trigger Section 1: This Page Refuses to Use Javascript"></a></li> | |
| <li class="p3n1 starter"><a href=#s2 accesskey=2 title="Trigger Section 2: :target Pseudo Selector"></a></li> | |
| <li class=p4n2><a href=#s3 accesskey=3 title="Trigger Section 3: ~ Swinton"></a></li> | |
| <li class=p5n3><a href=#s4 accesskey=4 title="Trigger Section 4: Getting Cray / On Fleek"></a></li> | |
| <li class=p6n4><a href=#s5 accesskey=5 title="Trigger Section 5: Accesskeys"></a></li> | |
| <li class=p7n5><a href=#s6 accesskey=6 title="Trigger Section 6: Limitations"></a></li> | |
| <li class=n6><a href=#s7 accesskey=7 title="Trigger Section 7: Acceptable Use Cases"></a></li> | |
| </ul> | |
| </nav> | |
| <nav class=thumbs aria-label="table of contents"> | |
| <ul> | |
| <li><a href=#s1 title="Thumbnail Trigger Section 1: This Page Refuses to Use Javascript"></a></li> | |
| <li><a href=#s2 title="Thumbnail Trigger Section 2: :target Pseudo Selector"></a></li> | |
| <li><a href=#s3 title="Thumbnail Trigger Section 3: ~ Swinton"></a></li> | |
| <li><a href=#s4 title="Thumbnail Trigger Section 4: Getting Cray / On Fleek"></a></li> | |
| <li><a href=#s5 title="Thumbnail Trigger Section 5: Accesskeys"></a></li> | |
| <li><a href=#s6 title="Thumbnail Trigger Section 6: Limitations"></a></li> | |
| <li><a href=#s7 title="Thumbnail Trigger Section 7: Acceptable Use Cases"></a></li> | |
| </ul> | |
| </nav> | |
| <main role=main> | |
| <section> | |
| <article> | |
| <h1>This pen refuses to use Javascript</h1> | |
| <p>To navigate, you can use the prev/next buttons or the thumbnails to the right. If you prefer to get crazy, you can also use <code>accesskey</code> to jump to sections by using the following keyboard shortcuts:</p> | |
| <pre> | |
| Mac: alt + ctrl + [1-7] | |
| PC Chrome/IE: alt + [1-7] | |
| PC FF: alt + shift + [1-7]</pre> | |
| <p class=right><small><a href="http://www.w3schools.com/tags/att_global_accesskey.asp" target="blank">More on accesskeys</a></small></p> | |
| </article> | |
| </section> | |
| <section> | |
| <article> | |
| <h1>:target</h1> | |
| <p>This interface relies on the CSS <code>:target</code> selector. An element is a “target” when its <code>id</code> is hash-referenced in the url.</p> | |
| <pre><a href="http://example.com#section-1">Section 1</a></pre> | |
| <pre>#section-1:target { | |
| display: block; | |
| }</pre> | |
| <p>In the above example, whenever the link is clicked, the content with an <code>id</code> of <code>section-1</code> will be displayed.</p> | |
| <p>CodePen removes the hash from the visible url, but right now you are looking at this pen with <code>#s2</code> as the target. If you were to manually add <code>#s2</code> to the end of this url and reload this pen, you will be taken to this screen instead of the first.</p> | |
| </article> | |
| </section> | |
| <section> | |
| <article> | |
| <h1>~ Swinton</h1> | |
| <p>In CSS, the tilde <code>~</code> is used as the “General sibling combinator”. In English, that means “any following sibling”.</p> | |
| <pre>.a ~ .b { | |
| display: none; | |
| }</pre> | |
| <p>This would hide any <code>.b</code> element that comes after an <code>.a</code> and is a sibling.</p> | |
| <pre> | |
| <div class="b">All good</div> | |
| <div class="a">All good</div> | |
| <div class="x"> | |
| <div class="b">Still good</div> | |
| </div> | |
| <div class="b">Hidden</div> | |
| </pre> | |
| <p>Only the last <code>.b</code> would be hidden. The first comes before the <code>.a</code>, and the second is not a direct sibling.</p> | |
| </article> | |
| </section> | |
| <section> | |
| <article> | |
| <h1>Getting Cray / On Fleek</h1> | |
| <p>By using <code>:target</code> and <code>~</code> we can do some pretty ridiculous things without touching Javascript. In this pen, we are using <code>:target</code> to trigger different state-related styles.</p> | |
| <p>At the top of this pen, there are <code><span></code> elements. Their only purpose is being used as targets.</p> | |
| <pre> | |
| <span id="s1" class="s"></span> | |
| <span id="s2" class="s"></span> | |
| ... | |
| </pre> | |
| <p>Using their <code>:target</code> state, we can then select and style anything else on the page.</p> | |
| <p>Moving the main content area over <code>-300%</code> to reveal this fourth section.</p> | |
| <pre>#s4:target ~ main { | |
| left: -300%; | |
| }</pre> | |
| <p>Changing the background color and image:</p> | |
| <pre>#s4:target ~ #background { | |
| background-image: url(http://lorempixel.com/g/400/150/city/4/); | |
| background-color: #265273; | |
| }</pre> | |
| <p>Changing the progress bar width:</p> | |
| <pre>#s4:target ~ #progress { | |
| width: 50%; | |
| }</pre> | |
| <p>Styling the fourth thumbnail as active:</p> | |
| <pre>#s4:target ~ .thumbs li:nth-child(4) a { | |
| opacity: 1; | |
| width: 80px; | |
| height: 60px; | |
| box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.1); | |
| }</pre> | |
| <p>Showing the appropriate previous link and making it say the right thing:</p> | |
| <pre>#s4:target ~ .prevnext li[class*="p4"] { | |
| display: block; | |
| float: left; | |
| } | |
| #s4:target ~ .prevnext li[class*="p4"] a:after { | |
| content: "Prev"; | |
| }</pre> | |
| <p>Showing the appropriate next link and making it say the right thing:</p> | |
| <pre>#s4:target ~ .prevnext li[class*="n4"] { | |
| display: block; | |
| float: right; | |
| } | |
| #s4:target ~ .prevnext li[class*="n4"] a:after { | |
| content: "Next"; | |
| }</pre> | |
| </article> | |
| </section> | |
| <section> | |
| <article> | |
| <h1>accesskeys</h1> | |
| <p>Each of the next/prev navigation items link to each of these spans and have an <code>accesskey</code> attribute. This allows them to be fired using the browser’s <code>accesskey</code> plus whatever key is provided. In this case, we are using the corresponding section number.</p> | |
| <pre> | |
| <a href="#s1" accesskey="1"></a> | |
| <a href="#s2" accesskey="2"></a> | |
| ... | |
| </pre> | |
| <p>Each browser and operating system use different keyboard shortcuts for <code>accesskey</code>, but the support is suprisingly universal.</p> | |
| <pre>Mac: alt + ctrl | |
| PC Chrome/IE: alt | |
| PC FF: alt + shift</pre> | |
| <p>To switch to the next section, you can hold down your <code>accesskey</code> combination above and then press <code>6</code>. | |
| <p>There’s more to read about <code>accesskey</code> on <a href="http://www.w3schools.com/tags/att_global_accesskey.asp" target="blank">w3schools</a>.</p> | |
| </article> | |
| </section> | |
| <section> | |
| <article> | |
| <h1>Limitations</h1> | |
| <p>As with any “Who needs JS?” approach, there are more than a few things to consider when doing something like this.</p> | |
| <h2>Accessibility</h2> | |
| <p>Confining yourself to CSS makes any interactive experience more inaccessible. There are a handful of ways one could optimize this for screen readers more than I (hardly) have, but it would be a lot of effort for a unsatisfactory result.</p> | |
| <p>Additionally, since the “prev/next” links are actually new links on each section change, the <code>:focus</code> that occurs when you have tabbed into them disappears because the link is actually disappearing. You need to re-tab in every time. Blech. No one likes excess tab unless we’re talking about <a href="https://en.wikipedia.org/wiki/Tab_(soft_drink)" title="Wikipedia: Tab Soda" target="blank">the soda</a>.</p> | |
| <h2>History</h2> | |
| <p>Each hashchange is going to be recorded in the browser history without some Javascript intervention. This can go either way as a pro or con. For something like this, it may be a pro.</p> | |
| <h2>Scalability</h2> | |
| <p>All ths content is being dumped into the DOM at once. At a certain size, this approach might not make a lot of sense. In theory, you could get around some of this with some smart selectors and <code>display: none;</code>, but depending on the complexity of your content, you may still have some load time issues. Dynamically loading content with Javascript is a often a very, very good thing.</p> | |
| </article> | |
| </section> | |
| <section> | |
| <article> | |
| <h1>Acceptable Use Cases</h1> | |
| <p>There are some cases where something this inaccessible is technically ok. This could make sense in an environment where it is only being used in a visual setting such as a live presentation or internal documentation tool.</p> | |
| <p>Most importantly, it can be used as a way to show that CSS is very powerful, but can also be very limiting—depending on the need.</p> | |
| <p>If you liked this, you may enjoy more of my bogus CSS in my <a href="http://codepen.io/collection/DzxNzN/" target="blank">No JS Collection</a>.</p> | |
| </article> | |
| </section> | |
| </main> |
| @import url(https://fonts.googleapis.com/css?family=Merriweather:300,300italic|Inconsolata); | |
| $border-color: rgba(#FFF,0.1); | |
| $mobile-w: 600px; | |
| $large-w: 1200px; | |
| $thumb-w: 80px; | |
| $thumb-h: 60px; | |
| $thumb-pad: 10px; | |
| $s-count: 7; | |
| main { | |
| position: absolute; | |
| top: 0; left: 0; bottom: 0; | |
| width: 100%; | |
| transition: left 500ms; | |
| } | |
| // | |
| // each section | |
| section { | |
| position: absolute; | |
| top: 0; right: 0; bottom: 0; | |
| width: 100%; | |
| margin: 0; | |
| box-sizing: border-box; | |
| overflow: scroll; | |
| -webkit-overflow-scrolling: touch; | |
| transition: opacity 250ms ease-in-out; | |
| article { | |
| width: calc(90% - #{$thumb-w + $thumb-pad}); | |
| max-width: 800px; | |
| padding: 4rem 0 4rem 5%; | |
| color: #fff; | |
| } | |
| code, pre { | |
| background: rgba(#000,0.5); | |
| border: 1px solid $border-color; | |
| border-radius: 4px; | |
| box-sizing: border-box; | |
| } | |
| code { | |
| padding: 0.25rem 0.5rem; | |
| } | |
| pre { | |
| padding: 1rem; | |
| margin: 1rem auto; | |
| } | |
| } | |
| // main nav ids | |
| .s { | |
| position: absolute; | |
| } | |
| // | |
| // navigation mixin styles | |
| @mixin active-next() { | |
| display: block; | |
| float: right; | |
| a:after { content: "Next"; } | |
| } | |
| @mixin active-prev() { | |
| display: block; | |
| float: left; | |
| a:after { content: "Prev"; } | |
| } | |
| @mixin active-thumb { | |
| opacity: 1; | |
| width: $thumb-w; | |
| height: $thumb-h; | |
| box-shadow: 0px 0px 0px 1px $border-color; | |
| } | |
| @mixin inactive-thumb { | |
| opacity: 0.5; | |
| width: $thumb-w / 2; | |
| height: $thumb-h / 2; | |
| box-shadow: 0px 0px 0px 1px rgba(#FFF,0); | |
| } | |
| // | |
| // navigation thumbs and buttons | |
| nav { | |
| position: fixed; | |
| z-index: 1; | |
| width: 100%; | |
| user-select: none; | |
| &.thumbs { | |
| right: $thumb-pad; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: $thumb-w + $thumb-pad; | |
| display: block; | |
| ul { | |
| margin: 0; padding: 0; | |
| } | |
| li { | |
| display: block; | |
| margin: 0 auto 0.5rem; | |
| &:first-child a { | |
| @include active-thumb(); | |
| } | |
| a { | |
| &:hover { | |
| opacity: 1; | |
| } | |
| @include inactive-thumb(); | |
| margin: 0 auto; | |
| display: block; | |
| background-blend-mode: multiply; | |
| background-position: center; | |
| background-size: cover; | |
| border-radius: 2px; | |
| position: relative; | |
| transition: | |
| height 250ms ease-in-out, | |
| width 250ms ease-in-out, | |
| opacity 250ms ease-in-out, | |
| box-shadow 250ms ease-in-out; | |
| &:active { | |
| transform: translateY(1px); | |
| } | |
| } | |
| } | |
| } | |
| &.prevnext { | |
| left: 0; | |
| top: 1rem; | |
| ul { | |
| list-style: none; | |
| margin: 0.5rem auto; | |
| padding: 0; | |
| width: 220px; | |
| position: relative; | |
| &:after { | |
| content: ""; | |
| display: table; | |
| clear: both; | |
| } | |
| li { | |
| display: none; | |
| width: 100px; | |
| &.starter { | |
| @include active-next(); | |
| } | |
| } | |
| a { | |
| display: block; | |
| width: 100%; | |
| box-sizing: border-box; | |
| padding: 0.5rem 1rem; | |
| border-radius: 4px; | |
| text-align: center; | |
| box-shadow: 0px 0px 0px 0px $border-color; | |
| border: 1px solid $border-color; | |
| color: #FFFFFF; | |
| background-color: rgba(#000,0.4); | |
| transition: background 200ms ease-in-out 200ms, | |
| box-shadow 200ms ease-in-out; | |
| &:hover, &:active { | |
| background-color: rgba(#000,0.5); | |
| } | |
| &:hover { | |
| box-shadow: 0px -2px 0px 0px $border-color; | |
| } | |
| &:active { | |
| box-shadow: 0px 0px 0px 0px $border-color; | |
| transform: translateY(1px); | |
| } | |
| } | |
| } | |
| } | |
| a { | |
| text-decoration: none; | |
| } | |
| } | |
| // | |
| // slider experience on non mobile | |
| @media (min-width: $mobile-w) { | |
| main { | |
| height: 100%; | |
| } | |
| } | |
| // | |
| // content position on large | |
| @media (min-width: $large-w) { | |
| main article { | |
| padding-left: 0; | |
| margin: 0 auto; | |
| } | |
| } | |
| // | |
| // prevnext buttons on non mobile | |
| @media (min-width: $mobile-w) { | |
| nav.prevnext { | |
| top: auto; | |
| bottom: 1rem; | |
| } | |
| } | |
| // | |
| // progress indicator at top of page | |
| #progress { | |
| position: fixed; | |
| z-index: 9; | |
| top: 0; | |
| left: 0; | |
| width: 0%; | |
| border-bottom: 12px solid rgba(#FFF,0.2); | |
| transition: width 1250ms linear; | |
| } | |
| // | |
| // :target and other iterative styles | |
| @for $i from 1 through $s-count { | |
| // | |
| // adjusting section opacity on all screens | |
| #s#{$i}:target ~ main section { | |
| opacity: 0; | |
| &:nth-child(#{$i}) { | |
| opacity: 1; | |
| } | |
| } | |
| // | |
| // adjusting section position on mobile screens | |
| @media (max-width: $mobile-w - 1) { | |
| #s#{$i}:target ~ main section { | |
| top: 50%; | |
| &:nth-child(#{$i}) { | |
| top: 0; | |
| } | |
| } | |
| } | |
| // | |
| // adjusting left position on big screens | |
| @media (min-width: $mobile-w) { | |
| // parent shift on target | |
| #s#{$i}:target ~ main { | |
| left: -100% * ($i - 1); | |
| } | |
| // individual | |
| section:nth-child(#{$i}) { | |
| left: 100% * ($i - 1); | |
| } | |
| } | |
| .thumbs li:nth-child(#{$i}) a { | |
| background-image: url(http://lorempixel.com/g/400/150/city/#{$i}/); | |
| background-color: hsl($i / $s-count * 360, 50, 30); | |
| } | |
| #s#{$i}:target ~ #background { | |
| background-image: url(http://lorempixel.com/g/400/150/city/#{$i}/); | |
| background-color: hsl($i / $s-count * 360, 50, 30); | |
| } | |
| #s#{$i}:target ~ #progress { | |
| width: ($i - 1) / ($s-count - 1) * 100%; | |
| } | |
| #s#{$i}:target ~ .thumbs li:first-child a { | |
| @include inactive-thumb(); | |
| } | |
| #s#{$i}:target ~ .thumbs li:nth-child(#{$i}) a { | |
| @include active-thumb(); | |
| } | |
| #s#{$i}:target ~ .prevnext li.starter { | |
| display: none; | |
| } | |
| #s#{$i}:target ~ .prevnext li[class*="p#{$i}"] { | |
| @include active-prev(); | |
| } | |
| #s#{$i}:target ~ .prevnext li[class*="n#{$i}"] { | |
| @include active-next(); | |
| } | |
| } | |
| // | |
| // background image | |
| #background { | |
| background-image: url(http://lorempixel.com/g/400/150/city/1/); | |
| background-color: hsl(1 / 5 * 360, 50, 30); | |
| background-blend-mode: multiply; | |
| background-position: center; | |
| background-size: cover; | |
| $blur: 10px; | |
| filter: blur($blur); | |
| position: fixed; | |
| z-index: -1; | |
| transition: | |
| background-image 500ms ease-in-out 500ms, | |
| background-color 500ms ease-in-out 1000ms; | |
| top: $blur * -2; right: $blur * -2; bottom: $blur * -2; left: $blur * -2; | |
| } | |
| // | |
| // global non-essential styles | |
| html, body { | |
| height: 100%; | |
| width: 100%; | |
| overflow: hidden; | |
| color: #FFF; | |
| font-family: Merriweather, Georgia, serif; | |
| font-weight: 300; | |
| } | |
| pre, code { | |
| font-family: Inconsolata, monospace; | |
| } | |
| a { | |
| color: #EEE; | |
| } | |
| h1, p { | |
| font-weight: 300; | |
| } | |
| p, ul { | |
| line-height: 1.6; | |
| } | |
| .right { text-align: right; } |