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

              
                .game
  form
    .board
      - for(let g = 1; g < 5; g++)
        svg.board__line(style=`--x: ${g < 3 ? g : 0}; --y: ${g < 3 ? 0 : g - 2}`)
          path(d='M 5 5 L 295 5', fill='none', stroke-width='10', stroke-linecap='round', stroke-dasharray='300', stroke-dashoffset='300')
      - for(let x = 0; x < 9; x++)
        input(type='checkbox', id=`x--${x}`, style=`--col: ${x % 3}; --row: ${Math.floor(x / 3)}`)
        span(style=`--col: ${x % 3}; --row: ${Math.floor(x / 3)}`)
          svg.x
            path.cross(d='M 20 20 L 80 80', fill='none', stroke-width='10', stroke-linecap='round', stroke-dasharray='100', stroke-dashoffset='100')
            path.cross(d='M 80 20 L 20 80', fill='none', stroke-width='10', stroke-linecap='round', stroke-dasharray='100', stroke-dashoffset='100')
        input(type='checkbox', id=`o--${x}`, style=`--col: ${x % 3}; --row: ${Math.floor(x / 3)}`)
        span(style=`--col: ${x % 3}; --row: ${Math.floor(x / 3)}`)
          svg.o
            circle.naught(cx='50', cy='50', r='30', fill='none', stroke-width='10', stroke-dasharray='200', stroke-dashoffset='200', stroke-linecap='round')
      - for(let y = 0; y < 9; y++)
        label(for=`x--${y}`, style=`--col: ${y % 3}; --row: ${Math.floor(y / 3)}`)
        label(for=`o--${y}`, style=`--col: ${y % 3}; --row: ${Math.floor(y / 3)}`)
      .board__result.board__result--x.result
        dl.result__content
          dt.result__title Winner!
          dd.result__details
            svg.x.result__icon
              path.cross(d='M 20 20 L 80 80', fill='none', stroke-width='10', stroke-linecap='round', stroke-dasharray='100', stroke-dashoffset='100')
              path.cross(d='M 80 20 L 20 80', fill='none', stroke-width='10', stroke-linecap='round', stroke-dasharray='100', stroke-dashoffset='100')
          button.result__reset(type='reset') Play again
        //- .result__shield
        - for (let c = 0; c < 30; c++)
          .result__firework-icon(style=`--y: ${Math.floor(Math.random() * 300) + 200}; --r: ${Math.floor(Math.random() * 360)}`) 🎉
      .board__result.board__result--o.result
        dl.result__content
          dt.result__title Winner!
          dd.result__details
            svg.o.result__icon
              circle.naught(cx='50', cy='50', r='30', fill='none', stroke-width='10', stroke-dasharray='200', stroke-dashoffset='200', stroke-linecap='round')
          button.result__reset(type='reset') Play again
        //- .result__shield
        - for (let c = 0; c < 30; c++)
          .result__firework-icon(style=`--y: ${Math.floor(Math.random() * 300) + 200}; --r: ${Math.floor(Math.random() * 360)}`) 🎉
      .board__result.board__result--draw
        dl.result__content
          dt.result__title Draw!
          dd.result__details
            .result__emoji 😭
          button.result__reset(type='reset') Play again
              
            
!

CSS

              
                *
  box-sizing border-box

$bg = #1f3a93
$naught = #f62459
$cross = #36dbd7
$line = #fafafa
$size = 300px
$animDuration = .5s

body
html
  align-items center
  background-color $bg
  color #fafafa
  display flex
  flex-direction column
  font-family 'Arial', sans-serif
  justify-content center
  height 100vh
  width 100vw
  overflow hidden
  position relative

svg
  height ($size / 3)
  width ($size / 3)

// hide all inputs from being visible
input
  opacity 0

label
  cursor pointer
  height ($size / 3)
  width ($size / 3)
  z-index 2

// position each cells elements using css variables
input
span
label
  left "calc(var(--col) * %s)" % ($size / 3)
  position absolute
  top "calc(var(--row) * %s)" % ($size / 3)

// give svg elements animation properties
circle
path
  animation-fill-mode forwards
  animation-name draw

circle
  animation-duration $animDuration
  stroke $naught

path
  animation-duration ($animDuration / 2)
  stroke $cross

button
  cursor pointer
  position absolute
  bottom 20px
  padding 8px 24px
  border-radius 4px
  border 0
  background $bg
  font-size .75rem
  color #fafafa
  filter drop-shadow(0 5px 5px black)
  transition transform .1s, filter .1s

  &:hover
    filter drop-shadow(0 5px 4px black)
    transform translateY(1px)

  &:active
    transform translateY(5px)
    filter drop-shadow(0 0 0 transparent)

// span displays which player is occupying a cell
span
  display none
  transform translate3d(0, 0, 3px)
// only once an input is checked for a cell, show the appropriate span
input:checked + span
  display block

.o
  transform rotateY(180deg) rotate(-35deg)

.x
  path:nth-of-type(2)
    animation-delay ($animDuration / 2)

.board
  height $size
  left 50%
  position absolute
  top 50%
  transform translate(-50%, -50%)
  transform-style preserve-3d
  width $size

  &__line
    height 10px
    left "calc(var(--x) * %s)" % ($size / 3)
    position absolute
    top "calc(var(--y) * %s)" % ($size / 3)
    width $size

    path
      stroke $line

    &:nth-of-type(1)
    &:nth-of-type(2)
      transform rotate(90deg) translate(-5px, 0)
      transform-origin left center
    &:nth-of-type(3)
    &:nth-of-type(4)
      transform translate(0, -50%)

    for $line in (1..4)
      &:nth-of-type({$line}) path
        animation-delay ($line * ($animDuration / 2))

  &__result
    animation fadeBg .25s $animDuration
    animation-fill-mode backwards
    background rgba(31, 58, 147, 1)
    display none
    height 100%
    left 0
    margin 0
    padding 0
    position absolute
    top 0
    transform translate3d(0, 0, 4px)
    width 100%

.result
  &__content
    align-items center
    animation flyIn .25s (.25s + $animDuration)
    animation-fill-mode backwards
    background #fafafa
    border-radius 10px
    color $bg
    display flex
    filter drop-shadow(0 10px 10px black)
    flex-direction column
    height 100%
    justify-content center
    left 0
    margin 0
    position absolute
    top 0
    width 100%
    z-index 3

  &__title
    font-size 1.5rem
    font-weight bolder
    text-transform uppercase

  &__details
    margin 0

  &__shield
    animation fadeBg .25s $animDuration
    animation-fill-mode backwards
    background rgba(31, 58, 147, 1)
    height 100%
    left 0
    position absolute
    top 0
    width 100%
    z-index 2

  &__emoji
    font-size 5rem

  &__icon
    path:nth-of-type(1)
      animation-delay (.5s + $animDuration)
    path:nth-of-type(2)
      animation-delay (0.9s + $animDuration)

    circle
      animation-delay (.5s + $animDuration)

  &__firework-icon
    animation fly .75s (.5s + $animDuration)
    animation-fill-mode backwards
    animation-timing-function cubic-bezier(0,.64,.25,1.01)
    font-size 2rem
    height 2rem
    left 50%
    margin-left -1rem
    margin-top -1rem
    opacity 0
    position absolute
    top 50%
    transform rotate(calc(var(--r) * 1deg)) translateY(calc(var(--y) * -1px))
    width 2rem

// animations
@keyframes draw
  to
    stroke-dashoffset 0

@keyframes fadeBg
  from
    background rgba(31, 58, 147, 0)

@keyframes fly
  0%
    opacity 0
    transform translateY(0)
  5%, 50%
    opacity 1
  100%
    opacity 0

@keyframes flyIn
  from
    transform translateY(100%) scale(0)

// there are 9 moves to make alternating between even and odd labels showing
:checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(even)
  transform translate3d(0, 0, 2px)

:checked ~ label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(even)
:checked ~ :checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ label:nth-of-type(odd)
  transform translate3d(0, 0, -1px)

:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked
  // if the last move is played and there isn't a winner, show a draw
  ~ .board__result--draw
    display block

// winning combos
#x--0:checked ~ #x--1:checked ~ #x--2:checked ~ .board__result--x
#x--3:checked ~ #x--4:checked ~ #x--5:checked ~ .board__result--x
#x--6:checked ~ #x--7:checked ~ #x--8:checked ~ .board__result--x
#x--0:checked ~ #x--3:checked ~ #x--6:checked ~ .board__result--x
#x--1:checked ~ #x--4:checked ~ #x--7:checked ~ .board__result--x
#x--2:checked ~ #x--5:checked ~ #x--8:checked ~ .board__result--x
#x--0:checked ~ #x--4:checked ~ #x--8:checked ~ .board__result--x
#x--2:checked ~ #x--4:checked ~ #x--6:checked ~ .board__result--x
#o--0:checked ~ #o--1:checked ~ #o--2:checked ~ .board__result--o
#o--3:checked ~ #o--4:checked ~ #o--5:checked ~ .board__result--o
#o--6:checked ~ #o--7:checked ~ #o--8:checked ~ .board__result--o
#o--0:checked ~ #o--3:checked ~ #o--6:checked ~ .board__result--o
#o--1:checked ~ #o--4:checked ~ #o--7:checked ~ .board__result--o
#o--2:checked ~ #o--5:checked ~ #o--8:checked ~ .board__result--o
#o--0:checked ~ #o--4:checked ~ #o--8:checked ~ .board__result--o
#o--2:checked ~ #o--4:checked ~ #o--6:checked ~ .board__result--o
  display block
  // if there's a winner on the last move, don't show a draw!
  ~ .board__result--draw
    display none
              
            
!

JS

              
                
              
            
!
999px

Console