Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs 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 its URL and the proper URL extension.

+ add another resource

JavaScript

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

Packages

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.

Behavior

Auto Save

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.

HTML

              
                mixin layerR(n, m, c, groups)
  if --n
    - let start = n % 4 == 0 ? 'start' : ''
    input(type='checkbox' class=`touch${n} ${start}`).touch
    - let end = m % 4 == 0 ? 'end' : ''
    .layer(class=`layer${n} ${end}`)
      if m % 4 == 0 
        a(href='#container').clear
        .timer
      if n == 1
        input(type='checkbox' disabled).touch.start
        .morse-code
          each g in groups
            - for (l = 1; l <= 4; l++)
              .code(class=`code-${g}-${l}`) 
        .translation
          each g in groups
            .letter(class=`letter-${g}`)   

      +layerR(n, ++m, c, groups)
      
mixin layer(n)
  input(type='reset' id='reset').reset
  a(href='#begin' id='begin').begin
  a(href='#container').clear
  .cleared
  - let groups = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
  +layerR(n + 1, 1, n, groups)
  .warning

form.container(id='container')
  +layer(40)
              
            
!

CSS

              
                @import url('https://fonts.googleapis.com/css2?family=Open+Sans&family=Roboto+Mono&display=swap')

html
  width: 100vw
  height: 100vh
  body
    width: 100%
    height: 100%
    display: flex
    box-sizing: border-box
    touch-action: none
    *,*::before,*::after
      box-sizing: inherit
      touch-action: inherit

@keyframes hide
  0%
    opacity: 1
    --position: initial
  99.999%
    opacity: 1
    --position: initial
  100%
    opacity: 0
    --position: absolute
  
@keyframes reveal
  0%
    opacity: 0
    --position: absolute
  99.999%
    opacity: 0
    --position: absolute
  100%
    opacity: 1
    --position: initial

@keyframes enable
  0%
    pointer-events: none
  99.999%
    pointer-events: none
  100%
    pointer-events: auto 
  
@keyframes disable
  0%
    color: black
  0.001%
    color: grey
  100%
    color: grey

$groups: (A, B, C, D, E, F, G, H, I, J)
@mixin arrange($groups: $groups)
  @each $g in $groups
    @for $l from 1 through 4
      @keyframes touch-#{$g}-#{$l}
        0%
          --code-#{$g}-#{$l}: var(--dot)
          --LEFT-#{$g}-#{$l}: initial 
          --RIGHT-#{$g}-#{$l}: #{' '}
        25%
          --code-#{$g}-#{$l}: var(--dash)
          --LEFT-#{$g}-#{$l}: #{' '}
          --RIGHT-#{$g}-#{$l}: initial
        100%
          --code-#{$g}-#{$l}: var(--dash)
          --LEFT-#{$g}-#{$l}: #{' '}
          --RIGHT-#{$g}-#{$l}: initial

$min: 2s
$extra: $min / 8
@mixin layerR($n, $m, $l, $i, $c, $groups: $groups, $min: $min, $extra: $extra)
  @if $n != 0
    $g: nth($groups, $i)
    .touch#{$n}
      @if $n % 4 == 0
        &.start 
          & ~ .layer#{$n}
            --start-#{$g}: paused
          &:checked ~ .layer#{$n}
            --start-#{$g}: running
      & ~ .layer#{$n}
        --state#{$n}: paused
        --checked-#{$g}-#{$l}: #{' '}
        --LEVEL-#{$g}-#{$l}: #{' '}
        --touched-#{$g}-#{$l}: + 0s
      &:active ~ .layer#{$n}
        --state#{$n}: running
        --start-#{$g}: paused
      &:checked ~ .layer#{$n}
        --checked-#{$g}-#{$l}: initial
        --LEVEL-#{$g}-#{$l - 1}: #{' '}
        --LEVEL-#{$g}-#{$l}: initial
        --touched-#{$g}-#{$l}: + #{$extra}

    .layer#{$n}
      $animation: ()
      $animation: append($animation, touch-#{$g}-#{$l} #{$min} var(--state#{$n}) linear forwards, comma)
      animation: $animation
      @if $m % 4 == 0
        $time: ($min)
        @for $l from 1 through 4
          $time: append($time, var(--touched-#{$g}-#{$l}))
        &.end
          --touched-#{$g}: calc(#{$time})
          animation: append($animation, enable var(--touched-#{$g}) var(--start-#{$g}) linear forwards, comma)
          &::before
            animation: reveal var(--touched-#{$g}) var(--start-#{$g}) linear forwards
          & > .start
            animation: reveal var(--touched-#{$g}) var(--start-#{$g}) linear forwards
          $animation: ()
          @if $i - 1 != 0
            $p: nth($groups, $i - 1)
            $animation: append($animation, reveal var(--touched-#{$p}) var(--start-#{$p}) linear forwards, comma)
          & > .timer
            animation: append($animation, expire var(--touched-#{$g}) var(--start-#{$g}) linear, comma)
            &::before
              animation: flash var(--touched-#{$g}) var(--start-#{$g}) linear backwards
          & > .clear
            $animation: append($animation, disable var(--touched-#{$g}) var(--start-#{$g}) linear backwards, comma)
            animation: $animation

    @include layerR($n - 1, $m + 1, if($m % 4 == 0, 1, $l + 1), if($m % 4 == 0, $i + 1, $i), $c)
    
@mixin layer($n, $groups: $groups)
  --dot: '\2022'
  --dash: '\002D'
  @include layerR($n, 1, 1, 1, $n)
  @each $g in $groups
    @for $l from 1 through 4
      .code-#{$g}-#{$l}
        animation: hide var(--touched-#{$g}) var(--start-#{$g}) linear forwards
        &::before
          content: var(--checked-#{$g}-#{$l}, var(--code-#{$g}-#{$l}))
    .letter-#{$g}
      @include binary-tree($g)
      animation: reveal var(--touched-#{$g}) var(--start-#{$g}) linear forwards
       
@mixin grid-area
  display: grid
  grid-template:
    columns: 1fr
    rows: repeat(3,1fr)
    areas: 'translation' 'morse-code' 'touch'
      
$values: (ROOT,E,T,I,A,N,M,S,U,R,W,D,K,G,O,H,V,F,'',L,'',P,J,B,X,C,Y,Z,Q,'','')    
@mixin binary-tree($group, $values: $values)
  $level: 1
  $count: 0
  $max: 1
  $length: length($values)
  --LEVEL-#{$group}-0: #{' '}
  @for $i from 1 through length($values)
    $value: nth($values, $i)
    $left: 2 * $i
    $right: 2 * $i + 1 
    --#{$value}-VALUE: '#{$value}'
    @if $left <= $length
      --#{$value}-LEFT: var(--LEFT-#{$group}-#{$level}, var(--#{nth($values, $left)}-NODE))
    @else
      --#{$value}-LEFT: #{' '}
    @if $right <= $length
      --#{$value}-RIGHT: var(--RIGHT-#{$group}-#{$level}, var(--#{nth($values, $right)}-NODE))
    @else
      --#{$value}-RIGHT: #{' '}
    --#{$value}-NODE: var(--LEVEL-#{$group}-#{$level - 1}, var(--#{$value}-VALUE)) var(--#{$value}-LEFT) var(--#{$value}-RIGHT)
    $count: $count + 1
    @if $count == $max
      $count: 0
      $max: $max * 2
      $level: $level + 1
  --LETTER: var(--ROOT-NODE)
    
.container
  @include grid-area
  --screen: 100vmin
  --break-point: clamp(0px, var(--screen) - 500px, 50vmin)
  --width: calc(var(--screen) - var(--break-point)) 
  width: var(--width)
  font-size: var(--width)
  height: calc(var(--width) * 1.2)
  border: 0.0175em solid grey
  background-color: white
  border-radius: 5% / 4%
  overflow: hidden
  margin: auto
  position: relative
  font-family: 'Open Sans', sans-serif
  @include layer(40)
  @include arrange
  &:target 
    *:not(.begin), *::before
      animation: unset !important
    .layer
      pointer-events: none !important
      opacity: 0
    .cleared, .reset
      visibility: visible
      
.layer
  @include grid-area
  width: 100%
  height: 100%
  position: absolute
  z-index: 3
  &:not(.end)
    pointer-events: none
    &::before
      opacity: 0
  & > .touch:not(.start):not(:disabled)
    opacity: 0
  &::before
    content: ''
    width: 100%
    height: 100%
    position: absolute
    pointer-events: none
    background-color: white
  
.cleared
  width: 100%
  height: 100%
  position: absolute
  background-color: white
  visibility: hidden
  display: flex
  justify-content: center
  align-items: center
  &::after
    content: 'CLEARED'
    transform: translatey(-300%)
    font-size: 12.5%
    color: black
    z-index: 1

.begin
  background-color: white
  position: absolute
  width: 100%
  height: 100%
  display: flex
  justify-content: center
  align-items: center
  animation: activate 1ms paused forwards
  text-decoration: none
  &:target
    pointer-events: none
    animation: unset
    &::before, &::after
      opacity: 0
  &:not(:target) 
    & ~ :is(.touch, .layer)
      opacity: 0
    & ~ .clear
      visibility: hidden
  &::after
    font-size: 12.5%
    content: 'Begin'
    color: black
    border: 0.25em solid transparent
    box-shadow: inset 0 0 0 0.1em grey, 0 0 0 0.1em grey
    width: calc(var(--width) / 2)
    height: calc(var(--width) / 2)
    border-radius: 100%
    display: flex
    justify-content: center
    align-items: center
  @keyframes activate
    0%
      --z-index: 0
    100% 
      --z-index: 2
  
.clear
  font-size: 6.5%
  position: absolute
  bottom: 22.5%
  right: 7.5%
  width: calc(var(--width) / 5)
  height: calc(var(--width) / 5)
  border-radius: 100%
  border: 0.12em solid grey
  background-color: lightgrey
  text-decoration: none
  display: flex
  justify-content: center
  align-items: center
  color: black
  &::before
    content: 'clear'
    transform: translatey(-5%)
  
.reset
  visibility: hidden
  position: absolute
  font-size: 12.5%
  font-family: inherit
  border-radius: 100%
  border: 0.25em solid grey
  width: calc(var(--width) / 2)
  height: calc(var(--width) / 2)
  top: 50%
  left: 50%
  transform: translate(-50%, -50%)
  z-index: 1
  background-color: white
  color: black
  cursor: pointer
  &:focus
    outline: none
  &:active ~ .begin
    animation-play-state: running
  &:not(:active) ~ .begin
    z-index: var(--z-index)
    
.timer
  position: absolute
  bottom: 22.5%
  left: 7.5%
  width: calc(var(--width) / 5)
  height: calc(var(--width) / 5)
  border-radius: 100%
  border: 0.12em solid grey
  background-color: lightgrey
  background-image: conic-gradient(grey var(--degree), transparent var(--degree) 360deg)
  display: flex
  justify-content: center
  align-items: center
  font-size: 6.5%
  @keyframes expire
    $p: 100% / 360 
    @for $i from 0 through 360
      #{$p * $i}
        --degree: #{$i * 1deg}
  &::before
    content: 'DECODING'
    font-size: 45%
    transform: translatey(7.5%)
    opacity: 0
    @keyframes flash
      @for $i from 0 through 6
        #{$i * (100% / 6)}
          opacity: #{$i % 2}

%array
  display: flex
  justify-content: center
  align-items: center
  border: 0.01em solid grey
  background-color: rgba(0,0,0,0.05)  
 
.morse-code
  @extend %array
  grid-area: morse-code
  margin: 5% 17.5%
  height: calc(var(--width) / 5)
  border-radius: 7.5% / 27.5%
  z-index: 4
  
.translation
  @extend %array
  grid-area: translation
  margin: 5% 5% 0
  height: calc(var(--width) / 3)
  border-radius: 7.5% / 17.5%
  z-index: 4
  
%character
  flex: 0 1
  position: var(--position)

.code
  @extend %character
  font-size: 37.5%
  font-family: initial
  
.letter
  @extend %character
  font-size: 12.5%
  font-family: 'Roboto Mono', monospace
  &::before
    content: var(--LETTER)

.touch
  width: calc(var(--width) / 3)
  height: calc(var(--width) / 3)
  border-radius: 100%
  justify-self: center
  align-self: center
  appearance: none
  grid-area: touch
  border: 0.5em solid grey
  background-color: lightgrey
  z-index: 2
  transform: translatey(-15%)
  position: relative
  &::before
    content: ''
    width: 100%
    height: 100%
    position: absolute
    border-radius: inherit
    pointer-events: none
    background-color: transparent
    box-shadow: inset 0 0 1em black, 0 0 0.5em black
    opacity: 0
    transition: 300ms opacity
  &:active::before
    opacity:1
  &:not(:disabled)
    cursor: pointer
  &:focus
    outline: none
  &:checked
    opacity: 0
    pointer-events: none
    & ~ .layer
      &:not(.end)
        pointer-events: auto
        &::before
          opacity: 1
      & > .touch
        opacity: 1
        
.warning
  font-size: 3.5%
  z-index: 99
  width: 100%
  height: 100%
  background-color: white
  position: absolute
  display: flex
  justify-content: center
  align-items: center
  --message: 'Warning! This app is not accessible from ' var(--browser) ' browsers. Please try again from a Chrome desktop browser.'
  &::before
    content: var(--message)
    flex: 0 0 80%
    text-align: center
  @media screen and (-webkit-min-device-pixel-ratio: 0) and (min-resolution: .001dpcm) 
    visibility: hidden
  @supports (-moz-appearance: none)
    visibility: visible
    --browser: 'Firefox'
  @supports (-ms-ime-align: auto)
    visibility: visible
    --browser: 'Microsoft Edge'
  @media not all and (min-resolution: .001dpcm)
    @supports (-webkit-appearance: none) and (stroke-color: transparent)
      visibility: visible
      --browser: 'Safari'
  @media (pointer: coarse)
    visibility: visible
    --browser: 'mobile'
              
            
!

JS

              
                
              
            
!
999px

Console